@devinnn/docdrift 0.1.6 → 0.1.11
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/README.md +66 -166
- package/dist/src/devin/schemas.js +22 -1
- package/dist/src/setup/devin-setup.js +157 -0
- package/dist/src/setup/index.js +20 -94
- package/dist/src/setup/onboard.js +132 -3
- package/dist/src/setup/setup-prompt.js +54 -0
- package/package.json +10 -3
package/README.md
CHANGED
|
@@ -2,198 +2,98 @@
|
|
|
2
2
|
|
|
3
3
|
Docs that never lie: detect drift between merged code and docs, then open low-noise, evidence-grounded remediation via Devin sessions.
|
|
4
4
|
|
|
5
|
+
## Table of contents
|
|
6
|
+
|
|
7
|
+
- [Deliverables](#deliverables)
|
|
8
|
+
- [Quick start](#quick-start)
|
|
9
|
+
- [Modes & spec providers](#modes--spec-providers)
|
|
10
|
+
- [Guides](#guides)
|
|
11
|
+
- [Project docs layout](#project-docs-layout)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
5
15
|
## Deliverables
|
|
6
16
|
|
|
7
17
|
- **npm package**: [@devinnn/docdrift](https://www.npmjs.com/package/@devinnn/docdrift) — TypeScript CLI (`docdrift`)
|
|
8
|
-
- `validate`
|
|
9
|
-
- `detect --base <sha> --head <sha>`
|
|
10
|
-
- `run --base <sha> --head <sha>`
|
|
11
|
-
- `status --since 24h`
|
|
18
|
+
- `validate` — Validate docdrift.yaml
|
|
19
|
+
- `detect --base <sha> --head <sha>` — Check for drift
|
|
20
|
+
- `run --base <sha> --head <sha>` — Full run with Devin
|
|
21
|
+
- `status --since 24h` — Show run status
|
|
12
22
|
- `sla-check` — Check for doc-drift PRs open 7+ days and open a reminder issue
|
|
13
|
-
-
|
|
14
|
-
-
|
|
23
|
+
- `setup` — Interactive setup (Devin analyzes repo, generates v2 docdrift.yaml)
|
|
24
|
+
- `generate-yaml` — Generate config from repo fingerprint `[--output path] [--force]`
|
|
25
|
+
- GitHub Action: `.github/workflows/devin-doc-drift.yml`
|
|
26
|
+
- Repo-local config: `docdrift.yaml`
|
|
15
27
|
- Demo API + OpenAPI exporter + driftable docs
|
|
16
|
-
- PR template + Loom script
|
|
17
|
-
|
|
18
|
-
## Why this is low-noise
|
|
19
|
-
|
|
20
|
-
- **Single session, single PR** — One Devin session handles the whole docsite (API reference + guides).
|
|
21
|
-
- **Gate on API spec diff** — We only run when OpenAPI drift is detected; no session for docs-check-only failures.
|
|
22
|
-
- **requireHumanReview** — When the PR touches guides/prose, we open an issue after the PR to direct attention.
|
|
23
|
-
- **7-day SLA** — If a doc-drift PR is open 7+ days, we open a reminder issue (configurable `slaDays`; use `sla-check` CLI or cron workflow).
|
|
24
|
-
- Confidence gating and allowlist/exclude enforcement.
|
|
25
|
-
- Idempotency key prevents duplicate actions for same repo/SHAs/action.
|
|
26
|
-
|
|
27
|
-
## Detection and gate
|
|
28
|
-
|
|
29
|
-
- **Gate:** We only run a Devin session when **OpenAPI drift** is detected. No drift → no session.
|
|
30
|
-
- Tier 1: OpenAPI drift (`openapi/generated.json` vs published spec)
|
|
31
|
-
- Tier 2: Heuristic path impacts from docAreas (e.g. `apps/api/src/auth/**` → guides)
|
|
32
|
-
|
|
33
|
-
Output artifacts (under `.docdrift/`):
|
|
34
|
-
|
|
35
|
-
- `drift_report.json`
|
|
36
|
-
- `metrics.json` (after `run`)
|
|
28
|
+
- PR template + [Loom script](loom.md)
|
|
37
29
|
|
|
38
|
-
|
|
30
|
+
---
|
|
39
31
|
|
|
40
|
-
##
|
|
41
|
-
|
|
42
|
-
1. Validate config and command availability.
|
|
43
|
-
2. Build drift report. **Gate:** If no OpenAPI drift, exit (no session).
|
|
44
|
-
3. Policy decision (`OPEN_PR | UPDATE_EXISTING_PR | OPEN_ISSUE | NOOP`).
|
|
45
|
-
4. Build one aggregated evidence bundle for the whole docsite.
|
|
46
|
-
5. One Devin session with whole-docsite prompt; poll to terminal status.
|
|
47
|
-
6. If PR opened and touches `requireHumanReview` paths → create issue to direct attention.
|
|
48
|
-
7. Surface result via GitHub commit comment; open issue on blocked/low-confidence paths.
|
|
49
|
-
8. Persist state (including `lastDocDriftPrUrl` for SLA); write `.docdrift/metrics.json`.
|
|
50
|
-
|
|
51
|
-
## Where the docs are (this repo)
|
|
52
|
-
|
|
53
|
-
| Path | Purpose |
|
|
54
|
-
| ------------------------------------------ | ----------------------------------------------------------------------- |
|
|
55
|
-
| `apps/docs-site/openapi/openapi.json` | Published OpenAPI spec (docdrift updates this when drift is detected). |
|
|
56
|
-
| `apps/docs-site/docs/api/` | API reference MDX generated from the spec (`npm run docs:gen`). |
|
|
57
|
-
| `apps/docs-site/docs/guides/auth.md` | Conceptual auth guide (updated only for conceptual drift). |
|
|
58
|
-
|
|
59
|
-
The docsite is a Docusaurus app with `docusaurus-plugin-openapi-docs`. The **generated** spec from code lives at `openapi/generated.json` (from `npm run openapi:export`). Drift = generated vs published differ. Verification runs `docs:gen` and `docs:build` so the docsite actually builds.
|
|
60
|
-
|
|
61
|
-
## How Devin updates them
|
|
62
|
-
|
|
63
|
-
1. **Evidence bundle** — Docdrift builds a tarball with the drift report, OpenAPI diff, and impacted doc snippets, and uploads it to the Devin API as session attachments.
|
|
64
|
-
2. **Devin session** — Devin is prompted (see `src/devin/prompts.ts`) to update only files under the allowlist (`openapi/**`, `apps/docs-site/**`), make minimal correct edits, run verification (`npm run docs:gen`, `npm run docs:build`), and open **one PR** per doc area with a clear description.
|
|
65
|
-
3. **PR** — Devin updates `apps/docs-site/openapi/openapi.json` to match the current API, runs `docs:gen` to regenerate API reference MDX, and opens a pull request. You review and merge; the docsite builds and the docs are updated.
|
|
66
|
-
|
|
67
|
-
So the “fix” is a **PR opened by Devin** that you merge; the repo’s docs don’t change until that PR is merged.
|
|
68
|
-
|
|
69
|
-
## Local usage
|
|
32
|
+
## Quick start
|
|
70
33
|
|
|
71
34
|
```bash
|
|
72
|
-
|
|
73
|
-
npx
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
35
|
+
# Interactive setup (requires DEVIN_API_KEY; add repo in Devin Machine first)
|
|
36
|
+
npx @devinnn/docdrift setup
|
|
37
|
+
|
|
38
|
+
# Or generate config only (scriptable)
|
|
39
|
+
npx @devinnn/docdrift generate-yaml --output docdrift.yaml --force
|
|
77
40
|
```
|
|
78
41
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
You can run a full end-to-end demo locally with no remote repo. Ensure `.env` has `DEVIN_API_KEY` (and optionally `GITHUB_TOKEN` only when you have a real repo).
|
|
82
|
-
|
|
83
|
-
1. **One-time setup (already done if you have two commits with drift)**
|
|
84
|
-
- Git is inited; baseline commit has docs in sync with API.
|
|
85
|
-
- A later commit changes `apps/api/src/model.ts` (e.g. `name` → `fullName`) and runs `npm run openapi:export`, so `openapi/generated.json` drifts from `docs/reference/openapi.json`.
|
|
86
|
-
|
|
87
|
-
2. **Run the pipeline**
|
|
88
|
-
|
|
89
|
-
```bash
|
|
90
|
-
npm install
|
|
91
|
-
npx tsx src/cli.ts validate
|
|
92
|
-
npx tsx src/cli.ts detect --base b0f624f --head 6030902
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
- Use your own `git log --oneline -3` to get `base` (older) and `head` (newer) SHAs if you recreated the demo.
|
|
42
|
+
→ [**Setup guide**](docs/guides/setup.md) — Setup options, prerequisites
|
|
96
43
|
|
|
97
|
-
|
|
98
|
-
Omit `GITHUB_TOKEN` so the CLI does not post comments or create issues. Devin session still runs; results are printed to stdout and written to `.docdrift/state.json` and `metrics.json`.
|
|
44
|
+
---
|
|
99
45
|
|
|
100
|
-
|
|
101
|
-
export $(grep -v '^#' .env | xargs)
|
|
102
|
-
unset GITHUB_TOKEN GITHUB_REPOSITORY GITHUB_SHA
|
|
103
|
-
npx tsx src/cli.ts run --base b0f624f --head 6030902
|
|
104
|
-
```
|
|
46
|
+
## Modes & spec providers
|
|
105
47
|
|
|
106
|
-
|
|
48
|
+
| Mode | When it runs |
|
|
49
|
+
| ---- | -------------- |
|
|
50
|
+
| **strict** (default) | Only when spec drift is detected (OpenAPI, GraphQL, etc.). No spec drift → no Devin session. |
|
|
51
|
+
| **auto** | Also when pathMappings match (file changes hit `match` patterns). |
|
|
107
52
|
|
|
108
|
-
|
|
109
|
-
- `.docdrift/drift_report.json` — drift items (e.g. OpenAPI `name` → `fullName`).
|
|
110
|
-
- `.docdrift/evidence/<runId>/` — evidence bundles and OpenAPI diff.
|
|
111
|
-
- Stdout — per–doc-area outcome (e.g. PR opened by Devin or blocked).
|
|
112
|
-
- `.docdrift/metrics.json` — counts and timing.
|
|
53
|
+
| Spec formats | openapi3, swagger2, graphql, fern, postman |
|
|
113
54
|
|
|
114
|
-
|
|
55
|
+
→ [**Configuration**](docs/guides/configuration.md) — Modes, spec providers, full config
|
|
115
56
|
|
|
116
|
-
|
|
117
|
-
- Push to `main` or run `workflow_dispatch`
|
|
118
|
-
- Action uploads:
|
|
119
|
-
- `.docdrift/drift_report.json`
|
|
120
|
-
- `.docdrift/evidence/**`
|
|
121
|
-
- `.docdrift/metrics.json`
|
|
57
|
+
---
|
|
122
58
|
|
|
123
|
-
##
|
|
59
|
+
## Guides
|
|
124
60
|
|
|
125
|
-
|
|
61
|
+
| Guide | What’s inside |
|
|
62
|
+
| ----- | -------------- |
|
|
63
|
+
| [Setup](docs/guides/setup.md) | `setup` vs `generate-yaml`, prerequisites |
|
|
64
|
+
| [Configuration](docs/guides/configuration.md) | Modes, spec providers; links to full schema |
|
|
65
|
+
| [How it works](docs/guides/how-it-works.md) | Detection, gate, core flow, low-noise design |
|
|
66
|
+
| [Ecosystems](docs/guides/ecosystems.md) | OpenAPI, FastAPI, Fern, GraphQL, Mintlify, Postman, monorepos |
|
|
67
|
+
| [Local development](docs/guides/local-development.md) | Local usage, demo without GitHub |
|
|
68
|
+
| [CI & GitHub](docs/guides/ci-github.md) | GitHub Actions, secrets, demo on GitHub |
|
|
69
|
+
| [Using in another repo](docs/guides/consuming-repo.md) | Published package, CLI, GitHub Actions |
|
|
70
|
+
| [Publishing](docs/guides/publishing.md) | Publishing the npm package |
|
|
71
|
+
| [Loom script](loom.md) | Recording script for demos |
|
|
126
72
|
|
|
127
|
-
|
|
128
|
-
git remote add origin https://github.com/your-org/docdrift.git
|
|
129
|
-
git push -u origin main
|
|
130
|
-
```
|
|
73
|
+
### Reference
|
|
131
74
|
|
|
132
|
-
|
|
133
|
-
Repo → **Settings** → **Secrets and variables** → **Actions** → **New repository secret**
|
|
134
|
-
- Name: `DEVIN_API_KEY`
|
|
135
|
-
- Value: your Devin API key (same as in `.env` locally)
|
|
75
|
+
- [docdrift.yaml](docdrift-yml.md) — Full configuration schema and validation
|
|
136
76
|
|
|
137
|
-
|
|
77
|
+
---
|
|
138
78
|
|
|
139
|
-
|
|
140
|
-
- **Push to `main`** — runs on every push (compares previous commit vs current).
|
|
141
|
-
- **Manual run** — **Actions** tab → **devin-doc-drift** → **Run workflow** (uses `HEAD` and `HEAD^` as head/base).
|
|
79
|
+
## Project docs layout (this repo)
|
|
142
80
|
|
|
143
|
-
|
|
81
|
+
| Path | Purpose |
|
|
82
|
+
| ---- | ------- |
|
|
83
|
+
| `apps/docs-site/openapi/openapi.json` | Published OpenAPI spec (docdrift updates when drift detected) |
|
|
84
|
+
| `apps/docs-site/docs/api/` | API reference MDX (`npm run docs:gen`) |
|
|
85
|
+
| `apps/docs-site/docs/guides/` | Conceptual guides (auth, etc.) |
|
|
144
86
|
|
|
145
|
-
|
|
87
|
+
Generated spec from code: `openapi/generated.json` (`npm run openapi:export`). Drift = generated vs published differ.
|
|
146
88
|
|
|
147
|
-
|
|
148
|
-
2. **Push this project with full history** (so both commits are on `main`):
|
|
149
|
-
```bash
|
|
150
|
-
git remote add origin https://github.com/YOUR_ORG/docdrift-demo.git
|
|
151
|
-
git push -u origin main
|
|
152
|
-
```
|
|
153
|
-
3. **Add secret** in that repo: **Settings** → **Secrets and variables** → **Actions** → `DEVIN_API_KEY` = your Devin API key.
|
|
154
|
-
4. **Trigger the workflow**
|
|
155
|
-
- Either push another small commit (e.g. README tweak), or
|
|
156
|
-
- **Actions** → **devin-doc-drift** → **Run workflow**.
|
|
157
|
-
5. **Where to look**
|
|
158
|
-
- **Actions** → open the run → **Run Doc Drift** step: the step logs print JSON with `sessionUrl`, `prUrl`, and `outcome` per doc area. Open any `sessionUrl` in your browser to see the Devin session.
|
|
159
|
-
- **Artifacts**: download **docdrift-artifacts** for `.docdrift/drift_report.json`, `.docdrift/metrics.json`, and evidence.
|
|
160
|
-
- **Devin dashboard**: sessions are tagged `docdrift`; you’ll see the run there once the step completes (often 1–3 minutes).
|
|
89
|
+
---
|
|
161
90
|
|
|
162
|
-
##
|
|
91
|
+
## Why low-noise
|
|
163
92
|
|
|
164
|
-
|
|
93
|
+
- **Single session, single PR** — One Devin session for the whole docsite
|
|
94
|
+
- **Gate on spec diff** — No session when no drift (strict mode)
|
|
95
|
+
- **requireHumanReview** — Issue when PR touches guides/prose
|
|
96
|
+
- **7-day SLA** — Reminder issue for stale doc-drift PRs
|
|
97
|
+
- **Confidence gating** — Allowlist, exclude, idempotency
|
|
165
98
|
|
|
166
|
-
|
|
167
|
-
2. **CLI**
|
|
168
|
-
```bash
|
|
169
|
-
npx @devinnn/docdrift validate
|
|
170
|
-
npx @devinnn/docdrift detect --base <base-sha> --head <head-sha>
|
|
171
|
-
# With env for run:
|
|
172
|
-
DEVIN_API_KEY=... GITHUB_TOKEN=... GITHUB_REPOSITORY=owner/repo GITHUB_SHA=<sha> npx @devinnn/docdrift run --base <base-sha> --head <head-sha>
|
|
173
|
-
```
|
|
174
|
-
3. **GitHub Actions** — add a step that runs the CLI (e.g. after checkout and setting base/head):
|
|
175
|
-
```yaml
|
|
176
|
-
- run: npx @devinnn/docdrift run --base ${{ steps.shas.outputs.base }} --head ${{ steps.shas.outputs.head }}
|
|
177
|
-
env:
|
|
178
|
-
DEVIN_API_KEY: ${{ secrets.DEVIN_API_KEY }}
|
|
179
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
180
|
-
GITHUB_REPOSITORY: ${{ github.repository }}
|
|
181
|
-
GITHUB_SHA: ${{ github.sha }}
|
|
182
|
-
```
|
|
183
|
-
Add repo secret `DEVIN_API_KEY`; `GITHUB_TOKEN` is provided by the runner.
|
|
184
|
-
|
|
185
|
-
## Publishing the package
|
|
186
|
-
|
|
187
|
-
- Set `"private": false` in `package.json` (or omit it).
|
|
188
|
-
- Set `"repository": { "type": "git", "url": "https://github.com/your-org/docdrift.git" }`.
|
|
189
|
-
- Run `pnpm build` (or `npm run build`), then `npm publish` (for a scoped package use `npm publish --access public`).
|
|
190
|
-
- Only the `dist/` directory is included (`files` in `package.json`). Consumers get the built CLI; they provide their own `docdrift.yaml` in their repo.
|
|
191
|
-
|
|
192
|
-
## Demo scenario
|
|
193
|
-
|
|
194
|
-
- Autogen drift: rename a field in `apps/api/src/model.ts`, merge to `main`, observe docs PR path.
|
|
195
|
-
- Conceptual drift: change auth behavior under `apps/api/src/auth/**`, merge to `main`, observe single escalation issue.
|
|
196
|
-
|
|
197
|
-
## Loom
|
|
198
|
-
|
|
199
|
-
See `/Users/cameronking/Desktop/sideproject/docdrift/loom.md` for the minute-by-minute recording script.
|
|
99
|
+
→ [**How it works**](docs/guides/how-it-works.md) — Detection, flow, evidence bundle
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PatchResultSchema = exports.PatchPlanSchema = void 0;
|
|
3
|
+
exports.PatchResultSchema = exports.SetupOutputSchema = exports.PatchPlanSchema = void 0;
|
|
4
4
|
exports.PatchPlanSchema = {
|
|
5
5
|
type: "object",
|
|
6
6
|
additionalProperties: false,
|
|
@@ -59,6 +59,27 @@ exports.PatchPlanSchema = {
|
|
|
59
59
|
},
|
|
60
60
|
},
|
|
61
61
|
};
|
|
62
|
+
/** Structured output for docdrift setup (Devin generates config files) */
|
|
63
|
+
exports.SetupOutputSchema = {
|
|
64
|
+
type: "object",
|
|
65
|
+
additionalProperties: false,
|
|
66
|
+
required: ["docdriftYaml", "summary"],
|
|
67
|
+
properties: {
|
|
68
|
+
docdriftYaml: { type: "string", description: "Full docdrift.yaml content, valid per schema" },
|
|
69
|
+
docDriftMd: {
|
|
70
|
+
type: "string",
|
|
71
|
+
description: "Content for .docdrift/DocDrift.md custom instructions (project-specific guidance for Devin)",
|
|
72
|
+
},
|
|
73
|
+
workflowYml: {
|
|
74
|
+
type: "string",
|
|
75
|
+
description: "Content for .github/workflows/docdrift.yml — must use npx @devinnn/docdrift for validate and run",
|
|
76
|
+
},
|
|
77
|
+
summary: {
|
|
78
|
+
type: "string",
|
|
79
|
+
description: "Brief summary of what you inferred (openapi paths, docsite, verification commands)",
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
};
|
|
62
83
|
exports.PatchResultSchema = {
|
|
63
84
|
type: "object",
|
|
64
85
|
additionalProperties: false,
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.runSetupDevin = runSetupDevin;
|
|
40
|
+
exports.runSetupDevinAndValidate = runSetupDevinAndValidate;
|
|
41
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
42
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
43
|
+
require("dotenv/config");
|
|
44
|
+
const v1_1 = require("../devin/v1");
|
|
45
|
+
const schemas_1 = require("../devin/schemas");
|
|
46
|
+
const setup_prompt_1 = require("./setup-prompt");
|
|
47
|
+
const generate_yaml_1 = require("./generate-yaml");
|
|
48
|
+
const index_1 = require("../index");
|
|
49
|
+
const onboard_1 = require("./onboard");
|
|
50
|
+
/** Resolve path to docdrift.schema.json in the package */
|
|
51
|
+
function getSchemaPath() {
|
|
52
|
+
// dist/src/setup -> ../../../ ; src/setup (tsx) -> ../..
|
|
53
|
+
const candidates = [
|
|
54
|
+
node_path_1.default.join(__dirname, "../../../docdrift.schema.json"),
|
|
55
|
+
node_path_1.default.join(__dirname, "../../docdrift.schema.json"),
|
|
56
|
+
];
|
|
57
|
+
const schemaPath = candidates.find((p) => node_fs_1.default.existsSync(p));
|
|
58
|
+
if (!schemaPath) {
|
|
59
|
+
throw new Error(`docdrift.schema.json not found. Tried: ${candidates.join(", ")}`);
|
|
60
|
+
}
|
|
61
|
+
return schemaPath;
|
|
62
|
+
}
|
|
63
|
+
function parseSetupOutput(session) {
|
|
64
|
+
const raw = session?.structured_output ?? session?.data?.structured_output;
|
|
65
|
+
if (!raw || typeof raw !== "object")
|
|
66
|
+
return null;
|
|
67
|
+
const o = raw;
|
|
68
|
+
const yaml = o.docdriftYaml;
|
|
69
|
+
const summary = o.summary;
|
|
70
|
+
if (typeof yaml !== "string" || typeof summary !== "string")
|
|
71
|
+
return null;
|
|
72
|
+
return {
|
|
73
|
+
docdriftYaml: yaml,
|
|
74
|
+
docDriftMd: typeof o.docDriftMd === "string" && o.docDriftMd ? o.docDriftMd : undefined,
|
|
75
|
+
workflowYml: typeof o.workflowYml === "string" && o.workflowYml ? o.workflowYml : undefined,
|
|
76
|
+
summary,
|
|
77
|
+
sessionUrl: "",
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
async function runSetupDevin(options) {
|
|
81
|
+
const cwd = options.cwd ?? process.cwd();
|
|
82
|
+
const outputPath = node_path_1.default.resolve(cwd, options.outputPath ?? "docdrift.yaml");
|
|
83
|
+
const apiKey = process.env.DEVIN_API_KEY?.trim();
|
|
84
|
+
if (!apiKey) {
|
|
85
|
+
throw new Error("DEVIN_API_KEY is required for setup. Set it in .env or export.");
|
|
86
|
+
}
|
|
87
|
+
const configExists = node_fs_1.default.existsSync(outputPath);
|
|
88
|
+
if (configExists && !options.force) {
|
|
89
|
+
const { confirm } = await Promise.resolve().then(() => __importStar(require("@inquirer/prompts")));
|
|
90
|
+
const overwrite = await confirm({
|
|
91
|
+
message: "docdrift.yaml already exists. Overwrite?",
|
|
92
|
+
default: false,
|
|
93
|
+
});
|
|
94
|
+
if (!overwrite) {
|
|
95
|
+
throw new Error("Setup cancelled.");
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
process.stdout.write("Uploading schema…\n");
|
|
99
|
+
const schemaPath = getSchemaPath();
|
|
100
|
+
const attachmentUrl = await (0, v1_1.devinUploadAttachment)(apiKey, schemaPath);
|
|
101
|
+
const prompt = (0, setup_prompt_1.buildSetupPrompt)([attachmentUrl]);
|
|
102
|
+
process.stdout.write("Creating Devin session…\n");
|
|
103
|
+
const session = await (0, v1_1.devinCreateSession)(apiKey, {
|
|
104
|
+
prompt,
|
|
105
|
+
unlisted: true,
|
|
106
|
+
max_acu_limit: 2,
|
|
107
|
+
tags: ["docdrift", "setup"],
|
|
108
|
+
attachments: [attachmentUrl],
|
|
109
|
+
structured_output: {
|
|
110
|
+
schema: schemas_1.SetupOutputSchema,
|
|
111
|
+
},
|
|
112
|
+
metadata: { purpose: "docdrift-setup" },
|
|
113
|
+
});
|
|
114
|
+
process.stdout.write("Devin is analyzing the repo and generating config…\n");
|
|
115
|
+
process.stdout.write(`Session: ${session.url}\n`);
|
|
116
|
+
const finalSession = await (0, v1_1.pollUntilTerminal)(apiKey, session.session_id, 15 * 60_000);
|
|
117
|
+
const result = parseSetupOutput(finalSession);
|
|
118
|
+
if (!result) {
|
|
119
|
+
throw new Error("Devin session did not return valid setup output. Check the session for details: " + session.url);
|
|
120
|
+
}
|
|
121
|
+
result.sessionUrl = session.url;
|
|
122
|
+
// Write files
|
|
123
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(outputPath), { recursive: true });
|
|
124
|
+
node_fs_1.default.writeFileSync(outputPath, result.docdriftYaml, "utf8");
|
|
125
|
+
if (result.docDriftMd) {
|
|
126
|
+
(0, onboard_1.ensureDocdriftDir)(cwd);
|
|
127
|
+
const docDriftPath = node_path_1.default.resolve(cwd, ".docdrift", "DocDrift.md");
|
|
128
|
+
node_fs_1.default.writeFileSync(docDriftPath, result.docDriftMd, "utf8");
|
|
129
|
+
}
|
|
130
|
+
if (result.workflowYml) {
|
|
131
|
+
const workflowsDir = node_path_1.default.resolve(cwd, ".github", "workflows");
|
|
132
|
+
node_fs_1.default.mkdirSync(workflowsDir, { recursive: true });
|
|
133
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(workflowsDir, "docdrift.yml"), result.workflowYml, "utf8");
|
|
134
|
+
(0, onboard_1.addSlaCheckWorkflow)(cwd);
|
|
135
|
+
}
|
|
136
|
+
(0, onboard_1.ensureGitignore)(cwd);
|
|
137
|
+
const validation = (0, generate_yaml_1.validateGeneratedConfig)(outputPath);
|
|
138
|
+
if (!validation.ok) {
|
|
139
|
+
throw new Error("Generated config failed validation:\n" + validation.errors.join("\n"));
|
|
140
|
+
}
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
async function runSetupDevinAndValidate(options) {
|
|
144
|
+
const result = await runSetupDevin(options);
|
|
145
|
+
const cwd = options.cwd ?? process.cwd();
|
|
146
|
+
const outputPath = node_path_1.default.resolve(cwd, options.outputPath ?? "docdrift.yaml");
|
|
147
|
+
if (outputPath === node_path_1.default.resolve(cwd, "docdrift.yaml")) {
|
|
148
|
+
try {
|
|
149
|
+
await (0, index_1.runValidate)();
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
153
|
+
throw err;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return result;
|
|
157
|
+
}
|
package/dist/src/setup/index.js
CHANGED
|
@@ -1,109 +1,35 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
4
|
};
|
|
38
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
6
|
exports.runSetup = runSetup;
|
|
40
7
|
const node_path_1 = __importDefault(require("node:path"));
|
|
41
|
-
const
|
|
42
|
-
const ai_infer_1 = require("./ai-infer");
|
|
43
|
-
const interactive_form_1 = require("./interactive-form");
|
|
44
|
-
const generate_yaml_1 = require("./generate-yaml");
|
|
45
|
-
const onboard_1 = require("./onboard");
|
|
46
|
-
const index_1 = require("../index");
|
|
8
|
+
const devin_setup_1 = require("./devin-setup");
|
|
47
9
|
async function runSetup(options = {}) {
|
|
48
10
|
const cwd = options.cwd ?? process.cwd();
|
|
49
11
|
const outputPath = node_path_1.default.resolve(cwd, options.outputPath ?? "docdrift.yaml");
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
});
|
|
57
|
-
if (!overwrite) {
|
|
58
|
-
console.log("Setup cancelled.");
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
process.stdout.write("Analyzing your repo…\n");
|
|
63
|
-
const fingerprint = (0, repo_fingerprint_1.buildRepoFingerprint)(cwd);
|
|
64
|
-
process.stdout.write("Generating suggestions…\n");
|
|
65
|
-
const inference = await (0, ai_infer_1.inferConfigFromFingerprint)(fingerprint, cwd);
|
|
66
|
-
const formResult = await (0, interactive_form_1.runInteractiveForm)(inference, cwd);
|
|
67
|
-
let config = (0, generate_yaml_1.buildConfigFromInference)(inference, formResult);
|
|
68
|
-
if (formResult.onboarding.addCustomInstructions) {
|
|
69
|
-
const devin = config.devin ?? {};
|
|
70
|
-
config.devin = {
|
|
71
|
-
...devin,
|
|
72
|
-
customInstructions: [".docdrift/DocDrift.md"],
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
(0, generate_yaml_1.writeConfig)(config, outputPath);
|
|
76
|
-
const { created } = (0, onboard_1.runOnboarding)(cwd, formResult.onboarding);
|
|
77
|
-
const validation = (0, generate_yaml_1.validateGeneratedConfig)(outputPath);
|
|
78
|
-
if (!validation.ok) {
|
|
79
|
-
console.error("Config validation failed:\n" + validation.errors.join("\n"));
|
|
80
|
-
throw new Error("Generated config is invalid. Fix the errors above or edit docdrift.yaml manually.");
|
|
81
|
-
}
|
|
82
|
-
if (outputPath === node_path_1.default.resolve(cwd, "docdrift.yaml")) {
|
|
83
|
-
try {
|
|
84
|
-
await (0, index_1.runValidate)();
|
|
85
|
-
}
|
|
86
|
-
catch (err) {
|
|
87
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
88
|
-
throw err;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
12
|
+
const result = await (0, devin_setup_1.runSetupDevinAndValidate)({
|
|
13
|
+
cwd,
|
|
14
|
+
outputPath: options.outputPath ?? "docdrift.yaml",
|
|
15
|
+
force: options.force,
|
|
16
|
+
openPr: options.openPr,
|
|
17
|
+
});
|
|
91
18
|
console.log("\ndocdrift setup complete\n");
|
|
92
19
|
console.log(" docdrift.yaml written and validated");
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
else if (item === ".gitignore")
|
|
99
|
-
console.log(" .gitignore updated");
|
|
100
|
-
else if (item.endsWith("docdrift.yml"))
|
|
101
|
-
console.log(" " + item + " added");
|
|
20
|
+
if (result.docDriftMd)
|
|
21
|
+
console.log(" .docdrift/DocDrift.md created (edit for custom instructions)");
|
|
22
|
+
if (result.workflowYml) {
|
|
23
|
+
console.log(" .github/workflows/docdrift.yml added");
|
|
24
|
+
console.log(" .github/workflows/docdrift-sla-check.yml added");
|
|
102
25
|
}
|
|
26
|
+
console.log(" .gitignore updated");
|
|
27
|
+
console.log("\nSummary: " + result.summary);
|
|
28
|
+
console.log("\nSession: " + result.sessionUrl);
|
|
103
29
|
console.log("\nNext steps:");
|
|
104
|
-
console.log(" 1.
|
|
105
|
-
console.log(" 2.
|
|
106
|
-
console.log(" 3. Run: docdrift validate — verify config");
|
|
107
|
-
console.log(" 4. Run: docdrift detect — check for drift");
|
|
108
|
-
console.log(" 5. Run: docdrift run — create Devin session (requires DEVIN_API_KEY)");
|
|
30
|
+
console.log(" 1. Add DEVIN_API_KEY to repo secrets (Settings > Secrets > Actions)");
|
|
31
|
+
console.log(" 2. Ensure your repo is set up in Devin (Devin's Machine > Add repository)");
|
|
32
|
+
console.log(" 3. Run: npx @devinnn/docdrift validate — verify config");
|
|
33
|
+
console.log(" 4. Run: npx @devinnn/docdrift detect — check for drift");
|
|
34
|
+
console.log(" 5. Run: npx @devinnn/docdrift run — create Devin session (requires DEVIN_API_KEY)");
|
|
109
35
|
}
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.ensureDocdriftDir = ensureDocdriftDir;
|
|
7
7
|
exports.createCustomInstructionsFile = createCustomInstructionsFile;
|
|
8
8
|
exports.ensureGitignore = ensureGitignore;
|
|
9
|
+
exports.addSlaCheckWorkflow = addSlaCheckWorkflow;
|
|
9
10
|
exports.addGitHubWorkflow = addGitHubWorkflow;
|
|
10
11
|
exports.runOnboarding = runOnboarding;
|
|
11
12
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
@@ -19,7 +20,129 @@ const GITIGNORE_BLOCK = `
|
|
|
19
20
|
.docdrift/state.json
|
|
20
21
|
.docdrift/run-output.json
|
|
21
22
|
`;
|
|
22
|
-
const WORKFLOW_CONTENT =
|
|
23
|
+
const WORKFLOW_CONTENT = `name: docdrift
|
|
24
|
+
|
|
25
|
+
on:
|
|
26
|
+
push:
|
|
27
|
+
branches: ["main"]
|
|
28
|
+
pull_request:
|
|
29
|
+
branches: ["main"]
|
|
30
|
+
workflow_dispatch:
|
|
31
|
+
|
|
32
|
+
jobs:
|
|
33
|
+
docdrift:
|
|
34
|
+
if: github.event_name != 'pull_request' || !startsWith(github.event.pull_request.head.ref, 'docdrift/')
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
permissions:
|
|
37
|
+
contents: write
|
|
38
|
+
pull-requests: write
|
|
39
|
+
issues: write
|
|
40
|
+
steps:
|
|
41
|
+
- uses: actions/checkout@v4
|
|
42
|
+
with:
|
|
43
|
+
fetch-depth: 0
|
|
44
|
+
|
|
45
|
+
- uses: actions/setup-node@v4
|
|
46
|
+
with:
|
|
47
|
+
node-version: "20"
|
|
48
|
+
|
|
49
|
+
- run: npm install
|
|
50
|
+
|
|
51
|
+
- name: Determine SHAs
|
|
52
|
+
id: shas
|
|
53
|
+
run: |
|
|
54
|
+
if [ "\$\{\{ github.event_name \}\}" = "pull_request" ]; then
|
|
55
|
+
HEAD_SHA="\$\{\{ github.event.pull_request.head.sha \}\}"
|
|
56
|
+
BASE_SHA="\$\{\{ github.event.pull_request.base.sha \}\}"
|
|
57
|
+
else
|
|
58
|
+
HEAD_SHA="\$\{\{ github.sha \}\}"
|
|
59
|
+
BASE_SHA="\$\{\{ github.event.before \}\}"
|
|
60
|
+
if [ -z "$BASE_SHA" ] || [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then
|
|
61
|
+
BASE_SHA="$(git rev-parse HEAD^)"
|
|
62
|
+
fi
|
|
63
|
+
fi
|
|
64
|
+
echo "head=\${HEAD_SHA}" >> $GITHUB_OUTPUT
|
|
65
|
+
echo "base=\${BASE_SHA}" >> $GITHUB_OUTPUT
|
|
66
|
+
echo "pr_number=\$\{\{ github.event.pull_request.number || '' \}\}" >> $GITHUB_OUTPUT
|
|
67
|
+
|
|
68
|
+
- name: Restore docdrift state
|
|
69
|
+
uses: actions/cache/restore@v4
|
|
70
|
+
id: docdrift-cache
|
|
71
|
+
with:
|
|
72
|
+
path: .docdrift
|
|
73
|
+
key: docdrift-state-\$\{\{ github.event_name \}\}-\$\{\{ github.event.pull_request.number || 'main' \}\}-\$\{\{ github.run_id \}\}
|
|
74
|
+
restore-keys: |
|
|
75
|
+
docdrift-state-\$\{\{ github.event_name \}\}-\$\{\{ github.event.pull_request.number || 'main' \}\}-
|
|
76
|
+
|
|
77
|
+
- name: Validate config
|
|
78
|
+
run: npx @devinnn/docdrift validate
|
|
79
|
+
|
|
80
|
+
- name: Run Doc Drift
|
|
81
|
+
env:
|
|
82
|
+
DEVIN_API_KEY: \$\{\{ secrets.DEVIN_API_KEY \}\}
|
|
83
|
+
GITHUB_TOKEN: \$\{\{ secrets.GITHUB_TOKEN \}\}
|
|
84
|
+
GITHUB_REPOSITORY: \$\{\{ github.repository \}\}
|
|
85
|
+
GITHUB_SHA: \$\{\{ github.sha \}\}
|
|
86
|
+
GITHUB_EVENT_NAME: \$\{\{ github.event_name \}\}
|
|
87
|
+
GITHUB_PR_NUMBER: \$\{\{ steps.shas.outputs.pr_number \}\}
|
|
88
|
+
run: |
|
|
89
|
+
PR_ARGS=""
|
|
90
|
+
if [ -n "$GITHUB_PR_NUMBER" ]; then
|
|
91
|
+
PR_ARGS="--trigger pull_request --pr-number $GITHUB_PR_NUMBER"
|
|
92
|
+
fi
|
|
93
|
+
npx @devinnn/docdrift run --base \$\{\{ steps.shas.outputs.base \}\} --head \$\{\{ steps.shas.outputs.head \}\} $PR_ARGS
|
|
94
|
+
|
|
95
|
+
- name: Save docdrift state
|
|
96
|
+
if: always()
|
|
97
|
+
uses: actions/cache/save@v4
|
|
98
|
+
with:
|
|
99
|
+
path: .docdrift
|
|
100
|
+
key: docdrift-state-\$\{\{ github.event_name \}\}-\$\{\{ github.event.pull_request.number || 'main' \}\}-\$\{\{ github.run_id \}\}
|
|
101
|
+
|
|
102
|
+
- name: Upload artifacts
|
|
103
|
+
if: always()
|
|
104
|
+
uses: actions/upload-artifact@v4
|
|
105
|
+
with:
|
|
106
|
+
name: docdrift-artifacts
|
|
107
|
+
path: |
|
|
108
|
+
.docdrift/drift_report.json
|
|
109
|
+
.docdrift/metrics.json
|
|
110
|
+
.docdrift/run-output.json
|
|
111
|
+
.docdrift/evidence/**
|
|
112
|
+
.docdrift/state.json
|
|
113
|
+
`;
|
|
114
|
+
const SLA_CHECK_WORKFLOW_CONTENT = `name: docdrift-sla-check
|
|
115
|
+
|
|
116
|
+
on:
|
|
117
|
+
schedule:
|
|
118
|
+
# Run daily at 09:00 UTC (checks for doc-drift PRs open 7+ days)
|
|
119
|
+
- cron: "0 9 * * *"
|
|
120
|
+
workflow_dispatch:
|
|
121
|
+
|
|
122
|
+
jobs:
|
|
123
|
+
sla-check:
|
|
124
|
+
runs-on: ubuntu-latest
|
|
125
|
+
permissions:
|
|
126
|
+
contents: read
|
|
127
|
+
issues: write
|
|
128
|
+
steps:
|
|
129
|
+
- uses: actions/checkout@v4
|
|
130
|
+
|
|
131
|
+
- uses: actions/setup-node@v4
|
|
132
|
+
with:
|
|
133
|
+
node-version: "20"
|
|
134
|
+
|
|
135
|
+
- run: npm install
|
|
136
|
+
|
|
137
|
+
- name: Validate config
|
|
138
|
+
run: npx @devinnn/docdrift validate
|
|
139
|
+
|
|
140
|
+
- name: Run SLA check
|
|
141
|
+
env:
|
|
142
|
+
GITHUB_TOKEN: \$\{\{ secrets.GITHUB_TOKEN \}\}
|
|
143
|
+
GITHUB_REPOSITORY: \$\{\{ github.repository \}\}
|
|
144
|
+
run: npx @devinnn/docdrift sla-check
|
|
145
|
+
`;
|
|
23
146
|
function ensureDocdriftDir(cwd) {
|
|
24
147
|
const dir = node_path_1.default.resolve(cwd, DOCDRIFT_DIR);
|
|
25
148
|
node_fs_1.default.mkdirSync(dir, { recursive: true });
|
|
@@ -54,11 +177,16 @@ function ensureGitignore(cwd) {
|
|
|
54
177
|
const toAppend = content.endsWith("\n") ? GITIGNORE_BLOCK.trimStart() : GITIGNORE_BLOCK;
|
|
55
178
|
node_fs_1.default.writeFileSync(gitignorePath, content + toAppend, "utf8");
|
|
56
179
|
}
|
|
180
|
+
function addSlaCheckWorkflow(cwd) {
|
|
181
|
+
const workflowsDir = node_path_1.default.resolve(cwd, ".github", "workflows");
|
|
182
|
+
node_fs_1.default.mkdirSync(workflowsDir, { recursive: true });
|
|
183
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(workflowsDir, "docdrift-sla-check.yml"), SLA_CHECK_WORKFLOW_CONTENT, "utf8");
|
|
184
|
+
}
|
|
57
185
|
function addGitHubWorkflow(cwd) {
|
|
58
186
|
const workflowsDir = node_path_1.default.resolve(cwd, ".github", "workflows");
|
|
59
187
|
node_fs_1.default.mkdirSync(workflowsDir, { recursive: true });
|
|
60
|
-
|
|
61
|
-
|
|
188
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(workflowsDir, "docdrift.yml"), WORKFLOW_CONTENT, "utf8");
|
|
189
|
+
addSlaCheckWorkflow(cwd);
|
|
62
190
|
}
|
|
63
191
|
function runOnboarding(cwd, choices) {
|
|
64
192
|
const created = [];
|
|
@@ -75,6 +203,7 @@ function runOnboarding(cwd, choices) {
|
|
|
75
203
|
if (choices.addWorkflow) {
|
|
76
204
|
addGitHubWorkflow(cwd);
|
|
77
205
|
created.push(".github/workflows/docdrift.yml");
|
|
206
|
+
created.push(".github/workflows/docdrift-sla-check.yml");
|
|
78
207
|
}
|
|
79
208
|
return { created };
|
|
80
209
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Prompt for Devin setup session — Devin analyzes the repo and generates
|
|
4
|
+
* docdrift.yaml, DocDrift.md, and GitHub workflow. The repo is already in
|
|
5
|
+
* Devin's Machine, so Devin has full context.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.buildSetupPrompt = buildSetupPrompt;
|
|
9
|
+
function attachmentBlock(urls) {
|
|
10
|
+
return urls.map((url, i) => `- ATTACHMENT ${i + 1}: ${url}`).join("\n");
|
|
11
|
+
}
|
|
12
|
+
function buildSetupPrompt(attachmentUrls) {
|
|
13
|
+
return [
|
|
14
|
+
"You are Devin. Task: set up docdrift for this repository.",
|
|
15
|
+
"",
|
|
16
|
+
"This repo is already loaded in your environment. Analyze it and produce the docdrift configuration files.",
|
|
17
|
+
"",
|
|
18
|
+
"ATTACHMENTS (read these for spec and schema):",
|
|
19
|
+
attachmentBlock(attachmentUrls),
|
|
20
|
+
"",
|
|
21
|
+
"REQUIREMENTS:",
|
|
22
|
+
"",
|
|
23
|
+
"1) docdrift.yaml (REQUIRED)",
|
|
24
|
+
" - Use version: 2",
|
|
25
|
+
" - Use specProviders format with format: openapi3",
|
|
26
|
+
" - Infer: current (type: export, command, outputPath), published path",
|
|
27
|
+
" - Set docsite to your docs root (e.g. docs, apps/docs-site)",
|
|
28
|
+
" - devin: apiVersion v1, unlisted true, maxAcuLimit 2, tags [docdrift]",
|
|
29
|
+
" - If you find an OpenAPI/swagger file or export script, use it",
|
|
30
|
+
" - policy: prCaps, confidence, allowlist (paths Devin may edit), verification.commands (e.g. npm run docs:build, npm run build)",
|
|
31
|
+
" - Add schema comment at top: # yaml-language-server: $schema=https://unpkg.com/@devinnn/docdrift/docdrift.schema.json",
|
|
32
|
+
"",
|
|
33
|
+
"2) .docdrift/DocDrift.md (RECOMMENDED)",
|
|
34
|
+
" - Starter custom instructions: PR title prefix [docdrift], tone, project-specific guidance",
|
|
35
|
+
" - If you include this, set devin.customInstructions: [.docdrift/DocDrift.md] in docdrift.yaml",
|
|
36
|
+
"",
|
|
37
|
+
"3) .github/workflows/docdrift.yml (RECOMMENDED)",
|
|
38
|
+
" - CRITICAL: Use npx @devinnn/docdrift (not npx docdrift)",
|
|
39
|
+
" - Steps: checkout, setup-node 20, Determine SHAs (base/head for push or PR), Validate config, Run Doc Drift",
|
|
40
|
+
" - Env: DEVIN_API_KEY, GITHUB_TOKEN, GITHUB_REPOSITORY, GITHUB_SHA",
|
|
41
|
+
" - Skip when PR head ref starts with docdrift/ (avoid feedback loop)",
|
|
42
|
+
" - Upload .docdrift artifacts (drift_report.json, metrics.json, evidence, state.json)",
|
|
43
|
+
" - Note: docdrift-sla-check.yml (daily cron for PRs open 7+ days) is added automatically",
|
|
44
|
+
"",
|
|
45
|
+
"OUTPUT:",
|
|
46
|
+
"Emit your final output in the provided structured output schema.",
|
|
47
|
+
"- docdriftYaml: complete YAML string (no leading/trailing comments about the task)",
|
|
48
|
+
"- docDriftMd: content for .docdrift/DocDrift.md, or empty string to omit",
|
|
49
|
+
"- workflowYml: content for .github/workflows/docdrift.yml, or empty string to omit",
|
|
50
|
+
"- summary: what you inferred (openapi export, docsite path, verification commands)",
|
|
51
|
+
"",
|
|
52
|
+
"Do NOT create files in the repo. Only produce the structured output.",
|
|
53
|
+
].join("\n");
|
|
54
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devinnn/docdrift",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Detect and remediate documentation drift with Devin sessions",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -17,7 +17,11 @@
|
|
|
17
17
|
],
|
|
18
18
|
"repository": {
|
|
19
19
|
"type": "git",
|
|
20
|
-
"url": ""
|
|
20
|
+
"url": "git+https://github.com/cameronking4/docdrift.git"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://github.com/cameronking4/docdrift#readme",
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/cameronking4/docdrift/issues"
|
|
21
25
|
},
|
|
22
26
|
"keywords": [
|
|
23
27
|
"docs",
|
|
@@ -27,6 +31,9 @@
|
|
|
27
31
|
"github-actions"
|
|
28
32
|
],
|
|
29
33
|
"license": "MIT",
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
30
37
|
"scripts": {
|
|
31
38
|
"build": "tsc -p tsconfig.json",
|
|
32
39
|
"test": "vitest run",
|
|
@@ -61,4 +68,4 @@
|
|
|
61
68
|
"vitest": "^3.0.5",
|
|
62
69
|
"zod-to-json-schema": "^3.25.1"
|
|
63
70
|
}
|
|
64
|
-
}
|
|
71
|
+
}
|