@docsworth/cli 0.2.0 → 0.3.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/README.md +60 -8
- package/dist/cli.js +52 -3
- package/dist/core/acl.d.ts +9 -0
- package/dist/core/acl.js +40 -6
- package/dist/core/adopt.d.ts +68 -0
- package/dist/core/adopt.js +665 -0
- package/dist/core/config.d.ts +6 -1
- package/dist/core/config.js +46 -2
- package/dist/core/edits.d.ts +13 -0
- package/dist/core/edits.js +20 -0
- package/dist/core/frontmatter.d.ts +12 -0
- package/dist/core/frontmatter.js +16 -7
- package/dist/core/gfm.d.ts +13 -0
- package/dist/core/gfm.js +244 -0
- package/dist/core/links.d.ts +82 -0
- package/dist/core/links.js +345 -0
- package/dist/core/lint.d.ts +1 -2
- package/dist/core/lint.js +40 -6
- package/dist/core/mcp/server.js +11 -2
- package/dist/core/printable.d.ts +4 -0
- package/dist/core/printable.js +16 -0
- package/dist/core/references.d.ts +38 -3
- package/dist/core/references.js +543 -41
- package/dist/core/repo.d.ts +4 -0
- package/dist/core/repo.js +3 -1
- package/dist/core/schema.d.ts +4 -0
- package/dist/core/schema.js +8 -6
- package/dist/core/search.d.ts +8 -0
- package/dist/core/search.js +34 -0
- package/dist/core/serve/server.d.ts +1 -2
- package/dist/core/serve/sessions.d.ts +5 -4
- package/dist/core/serve/sessions.js +102 -29
- package/dist/core/service.d.ts +118 -20
- package/dist/core/service.js +249 -91
- package/dist/core/stale.js +2 -1
- package/dist/core/tool-registry.js +6 -3
- package/dist/core/verify-pages.d.ts +19 -0
- package/dist/core/verify-pages.js +110 -0
- package/dist/core/version.d.ts +2 -2
- package/dist/core/version.js +2 -2
- package/dist/web/assets/index-8J-FUbb0.js +29 -0
- package/dist/web/assets/index-BGpAZzeM.css +1 -0
- package/dist/web/index.html +2 -2
- package/docs/format-spec.md +40 -2
- package/package.json +2 -2
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/bin/docsworth-mcp.mjs +1 -1
- package/dist/web/assets/index-BglLMIyN.css +0 -1
- package/dist/web/assets/index-DugFPoRu.js +0 -25
package/README.md
CHANGED
|
@@ -44,7 +44,8 @@ docsworth init --team platform # config, CODEOWNERS, root map, a guide
|
|
|
44
44
|
docsworth lint . # 3 pages, 0 errors, 0 warnings
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
`init` reads your GitHub org from the repo's remote; in a repo without one yet, add
|
|
48
|
+
`--org <your-org>`. Add `--ci` for a workflow that lints and compiles on every PR.
|
|
48
49
|
|
|
49
50
|
Install the Claude Code plugin from this repo's marketplace:
|
|
50
51
|
|
|
@@ -67,12 +68,54 @@ Start Claude Code in the knowledge repo, or set `DOCSWORTH_KNOWLEDGE_DIR` to its
|
|
|
67
68
|
The server uses your own GitHub identity (`GITHUB_TOKEN`, `GH_TOKEN` or `gh auth
|
|
68
69
|
token`), so your repo and team permissions decide what you see.
|
|
69
70
|
|
|
71
|
+
## Adopting existing docs
|
|
72
|
+
|
|
73
|
+
Docs you already have, a Docusaurus site's say, become pages without moving a URL. With
|
|
74
|
+
them under `knowledge/` (or `knowledge_root` in `.knowledge/config.yaml` set to where they
|
|
75
|
+
are), after `init`:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
docsworth adopt --team docs # the frontmatter each page lacks, as drafts
|
|
79
|
+
docsworth lint .
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`adopt` gives each page the fields it lacks, and keeps everything it has:
|
|
83
|
+
|
|
84
|
+
- a page's `id` is its own, else its file name, a Docusaurus number prefix removed, so the
|
|
85
|
+
site's URLs stay where they are;
|
|
86
|
+
- its owner comes from CODEOWNERS, else `--team`;
|
|
87
|
+
- a route link like `/guides/setup` becomes a relative link to the file,
|
|
88
|
+
`../guides/setup.md`;
|
|
89
|
+
- keys a site generator keeps, such as `sidebar_position`, go in `frontmatter.extra_fields`;
|
|
90
|
+
- images a site serves from its `static/` directory (`/img/x.png`) get a hint naming the
|
|
91
|
+
`static_dirs` line to add, after which lint reads them there.
|
|
92
|
+
|
|
93
|
+
A page or link it can't settle stays as it is, listed, and lint fails it until it's fixed
|
|
94
|
+
by hand: a category index like `guides/index.md`, two pages with one file name, a link to
|
|
95
|
+
nothing. `--dry-run` reports and writes nothing.
|
|
96
|
+
|
|
97
|
+
Adopted pages are drafts, so agents cite none of them yet. Once a team has read its pages:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
docsworth verify knowledge/guides # canonical, verified today
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Both commands only change files. Commit them and open a pull request, which CODEOWNERS
|
|
104
|
+
sends to the owning team.
|
|
105
|
+
|
|
106
|
+
If the site stays up, set `site_url` (`https://docs.example.com/docs/`) in the config.
|
|
107
|
+
Then a link to a page someone can't see reads `[restricted page]` by its full URL on the
|
|
108
|
+
site, as it already does by its route. A link without an extension that names nothing
|
|
109
|
+
reads `[restricted page]` too: a route can't be told from a hidden page's.
|
|
110
|
+
|
|
70
111
|
## Commands
|
|
71
112
|
|
|
72
113
|
| Command | What it does |
|
|
73
114
|
| --- | --- |
|
|
74
115
|
| `docsworth init [dir]` | Scaffold config, CODEOWNERS, `knowledge/index.md`, a glossary and a writing guide. Never overwrites. |
|
|
75
116
|
| `docsworth lint [dir]` | Frontmatter, CODEOWNERS ownership, links and duplicate ids. Exit 1 on errors; overdue pages only warn. |
|
|
117
|
+
| `docsworth adopt [dir] [--team t]` | Existing pages get the frontmatter they lack, as drafts, and route links become `.md` links; lists what it can't settle. Working tree only; `--dry-run`. |
|
|
118
|
+
| `docsworth verify <paths…>` | The draft or canonical pages under the paths become canonical, verified today. Working tree only; `--dry-run`. |
|
|
76
119
|
| `docsworth stale [dir]` | Pages past their review SLA or without a team owner. |
|
|
77
120
|
| `docsworth coverage [dir] [--html f]` | Percentage team-owned and percentage verified within SLA, by team; optional static dashboard. |
|
|
78
121
|
| `docsworth digest [dir] [--send]` | Weekly per-team digest: due, overdue, most read by agents. Prints unless `--send`. |
|
|
@@ -131,11 +174,20 @@ npm run docsworth -- --as-of 2026-11-02 audit examples/acme --out audit
|
|
|
131
174
|
## Browser extension
|
|
132
175
|
|
|
133
176
|
A side panel for Chrome (and other Chromium browsers) over `docsworth serve`. From the panel
|
|
134
|
-
you can search and read pages, or suggest an addition to one.
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
177
|
+
you can search and read pages, or suggest an addition to one.
|
|
178
|
+
|
|
179
|
+
**Saving what you read.**
|
|
180
|
+
- **From the menu:** on any page, right-click and choose **Save page to Docsworth**, or
|
|
181
|
+
select some text and choose **Save selection to Docsworth**.
|
|
182
|
+
- **From the panel:** **Save this page** and **Save the selection** read the tab you opened
|
|
183
|
+
the panel from with the toolbar button.
|
|
184
|
+
- **In Gmail:** a **Save to Docsworth** button beside a thread's subject.
|
|
185
|
+
|
|
186
|
+
The page's main content or the selection arrives as markdown, with its headings, links,
|
|
187
|
+
code blocks and tables, and without what the page hides or its navigation. The panel first
|
|
188
|
+
shows the pages that may already cover it, each with **Add the capture here**, then a draft
|
|
189
|
+
page filled from it, with its source. Nothing is written until you open the PR. The
|
|
190
|
+
extension isn't in the Chrome Web Store yet: build it and load it unpacked.
|
|
139
191
|
|
|
140
192
|
```bash
|
|
141
193
|
npm run build -w @docsworth/extension
|
|
@@ -177,8 +229,8 @@ See [AGENTS.md](AGENTS.md), and [docs/releasing.md](docs/releasing.md) for relea
|
|
|
177
229
|
|
|
178
230
|
## Status
|
|
179
231
|
|
|
180
|
-
v0.
|
|
181
|
-
[the release notes](docs/release-notes/v0.
|
|
232
|
+
v0.3. What's in it, and what hasn't been verified against the real thing yet, is in
|
|
233
|
+
[the release notes](docs/release-notes/v0.3.0.md).
|
|
182
234
|
|
|
183
235
|
## License
|
|
184
236
|
|
package/dist/cli.js
CHANGED
|
@@ -5,6 +5,7 @@ import { dirname, join, resolve } from "node:path";
|
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { Command, InvalidArgumentError } from "commander";
|
|
7
7
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
|
+
import { adoptRepo } from "./core/adopt.js";
|
|
8
9
|
import { actorOf, createAuditLog, stateDir } from "./core/audit-log.js";
|
|
9
10
|
import { CONFIG_PATH, ConfigError, loadConfig } from "./core/config.js";
|
|
10
11
|
import { computeCoverage, formatCoverage } from "./core/coverage.js";
|
|
@@ -21,6 +22,8 @@ import { DEFAULT_PORT, startServer } from "./core/serve/server.js";
|
|
|
21
22
|
import { loadToken } from "./core/serve/token.js";
|
|
22
23
|
import { KnowledgeService } from "./core/service.js";
|
|
23
24
|
import { listStale } from "./core/stale.js";
|
|
25
|
+
import { printable, printableJson } from "./core/printable.js";
|
|
26
|
+
import { verifyPages } from "./core/verify-pages.js";
|
|
24
27
|
import { PACKAGE_VERSION } from "./core/version.js";
|
|
25
28
|
import { existsSync, readFileSync } from "node:fs";
|
|
26
29
|
import { parse as parseYaml } from "yaml";
|
|
@@ -172,9 +175,55 @@ program
|
|
|
172
175
|
.option("--json", "machine-readable output")
|
|
173
176
|
.action((dir, opts) => {
|
|
174
177
|
const result = lintRepo(repoAt(dir), now());
|
|
175
|
-
console.log(opts.json ?
|
|
178
|
+
console.log(opts.json ? printableJson(result) : formatDiagnostics(result));
|
|
176
179
|
process.exitCode = result.errors > 0 ? 1 : 0;
|
|
177
180
|
});
|
|
181
|
+
program
|
|
182
|
+
.command("adopt")
|
|
183
|
+
.description("give existing markdown pages the frontmatter Docsworth needs, as drafts, and turn route links into .md links (working tree only)")
|
|
184
|
+
.argument("[dir]", "knowledge repo root", ".")
|
|
185
|
+
.option("--team <team>", "owning team for pages CODEOWNERS gives no single team")
|
|
186
|
+
.option("--review-every <period>", "review period for pages without one", "90d")
|
|
187
|
+
.option("--dry-run", "report what would change; write nothing")
|
|
188
|
+
.option("--json", "machine-readable output")
|
|
189
|
+
.action((dir, opts) => {
|
|
190
|
+
const r = adoptRepo(resolve(dir), { now: now(), team: opts.team, reviewEvery: opts.reviewEvery, dryRun: opts.dryRun });
|
|
191
|
+
if (opts.json)
|
|
192
|
+
return console.log(printableJson(r));
|
|
193
|
+
// Paths, links, keys and reasons quote what pages hold: printed escaped.
|
|
194
|
+
const say = (line) => console.log(printable(line));
|
|
195
|
+
for (const l of r.left)
|
|
196
|
+
say(`left ${l.path}: ${l.reason}`);
|
|
197
|
+
for (const l of r.linksLeft)
|
|
198
|
+
say(`link left ${l.path}: \`${l.link}\` ${l.why}`);
|
|
199
|
+
if (r.extraFields.length)
|
|
200
|
+
say(`config frontmatter.extra_fields gains ${r.extraFields.join(", ")}`);
|
|
201
|
+
for (const h of r.hints)
|
|
202
|
+
say(`hint ${h}`);
|
|
203
|
+
const n = (count, what) => `${count} ${what}${count === 1 ? "" : "s"}`;
|
|
204
|
+
console.log(`\n${r.dryRun ? "Would adopt" : "Adopted"} ${n(r.adopted.length, "page")} (${r.unchanged.length} unchanged, ${r.left.length} left); ` +
|
|
205
|
+
`${r.linksRewritten} ${r.linksRewritten === 1 ? "link" : "links"} rewritten, ${r.linksLeft.length} left.`);
|
|
206
|
+
console.log(r.dryRun
|
|
207
|
+
? "Dry run: nothing written."
|
|
208
|
+
: `Nothing is committed. Review the diff, run \`docsworth lint ${dir}\`, and open a pull request: adopted pages are drafts, cited once their team runs \`docsworth verify\`.`);
|
|
209
|
+
});
|
|
210
|
+
program
|
|
211
|
+
.command("verify")
|
|
212
|
+
.description("verify pages: each draft or canonical page under the paths becomes canonical, verified today (working tree only)")
|
|
213
|
+
.argument("<paths...>", "pages, or directories of them")
|
|
214
|
+
.option("--dir <dir>", "knowledge repo root", ".")
|
|
215
|
+
.option("--dry-run", "report what would change; write nothing")
|
|
216
|
+
.option("--json", "machine-readable output")
|
|
217
|
+
.action((paths, opts) => {
|
|
218
|
+
// Paths are the shell's, relative to where it runs, whatever --dir says.
|
|
219
|
+
const r = verifyPages(resolve(opts.dir), paths.map((p) => resolve(p)), { now: now(), dryRun: opts.dryRun });
|
|
220
|
+
if (opts.json)
|
|
221
|
+
return console.log(printableJson(r));
|
|
222
|
+
for (const l of r.left)
|
|
223
|
+
console.log(printable(`left ${l.path}: ${l.reason}`));
|
|
224
|
+
console.log(`\n${r.dryRun ? "Would verify" : "Verified"} ${r.verified.length} page${r.verified.length === 1 ? "" : "s"} (${r.unchanged.length} already verified today, ${r.left.length} left).`);
|
|
225
|
+
console.log(r.dryRun ? "Dry run: nothing written." : "Nothing is committed: open a pull request, which the owning team reviews.");
|
|
226
|
+
});
|
|
178
227
|
program
|
|
179
228
|
.command("stale")
|
|
180
229
|
.description("pages past their review SLA or without a team owner")
|
|
@@ -295,7 +344,6 @@ program
|
|
|
295
344
|
const web = join(dirname(fileURLToPath(import.meta.url)), "web");
|
|
296
345
|
const serving = await startServer({
|
|
297
346
|
root,
|
|
298
|
-
knowledgeRoot: config.knowledge_root,
|
|
299
347
|
identity,
|
|
300
348
|
who,
|
|
301
349
|
audit,
|
|
@@ -400,7 +448,8 @@ const isMain = process.argv[1] && /(?:cli\.(?:ts|js)|docsworth)$/.test(process.a
|
|
|
400
448
|
if (isMain) {
|
|
401
449
|
program.parseAsync().catch(async (err) => {
|
|
402
450
|
if (isUserError(err)) {
|
|
403
|
-
|
|
451
|
+
// A message can quote a path or a value a page holds: printed escaped.
|
|
452
|
+
console.error(`docsworth: ${printable(err.message)}`);
|
|
404
453
|
// Exit 3 if the failure also left a change its required audit log doesn't show.
|
|
405
454
|
process.exit(process.exitCode === EXIT_AUDIT_DEGRADED ? EXIT_AUDIT_DEGRADED : 2);
|
|
406
455
|
}
|
package/dist/core/acl.d.ts
CHANGED
|
@@ -26,3 +26,12 @@ export declare const RESTRICTED = "[restricted page]";
|
|
|
26
26
|
* definitions, and HTML anchors.
|
|
27
27
|
*/
|
|
28
28
|
export declare function redactLinks(body: string, fromPath: string, repo: Repo, who: Identity): string;
|
|
29
|
+
/** One pass of the sweep for `who` on page `fromPath`: `liberal`, every route naming nothing fails closed, a link or not. */
|
|
30
|
+
export declare function redactionSweep(fromPath: string, repo: Repo, who: Identity, gone?: Set<string>): (text: string, liberal: boolean) => string;
|
|
31
|
+
/**
|
|
32
|
+
* `sweep` run until it changes nothing. Replacing a line can change what the rest means (a
|
|
33
|
+
* fence gone, code is markdown, and its links are links), so a pass can leave work for the
|
|
34
|
+
* next. Past `passes`, one last `liberal` pass, where every route naming nothing fails
|
|
35
|
+
* closed, ends it.
|
|
36
|
+
*/
|
|
37
|
+
export declare function untilStable(text: string, sweep: (text: string, liberal: boolean) => string, passes?: number): string;
|
package/dist/core/acl.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ACL_INTERNAL } from "./schema.js";
|
|
2
|
-
import { destinationHides, namingLines, pageNames, replaceLines } from "./references.js";
|
|
2
|
+
import { definitionsIn, destinationHides, linkPaths, normalLabel, namingLines, pageNames, replaceLines } from "./references.js";
|
|
3
3
|
export function canSee(page, who) {
|
|
4
4
|
if (!who.repoAccess)
|
|
5
5
|
return false;
|
|
@@ -38,8 +38,8 @@ const A_CLOSE = /<\/a\s*>/i;
|
|
|
38
38
|
* path. Anything under the knowledge root that is not a loadable page fails closed: an
|
|
39
39
|
* invalid page has no trustworthy ACL.
|
|
40
40
|
*/
|
|
41
|
-
function hidesPage(dest, fromPath, repo, who, html = false) {
|
|
42
|
-
return destinationHides(dest, fromPath, namesFor(repo, who), html);
|
|
41
|
+
function hidesPage(dest, fromPath, repo, who, html = false, links) {
|
|
42
|
+
return destinationHides(dest, fromPath, namesFor(repo, who), html, links);
|
|
43
43
|
}
|
|
44
44
|
/**
|
|
45
45
|
* Rewrite every link to a page the caller cannot see, so a visible page (say the root
|
|
@@ -48,26 +48,60 @@ function hidesPage(dest, fromPath, repo, who, html = false) {
|
|
|
48
48
|
* definitions, and HTML anchors.
|
|
49
49
|
*/
|
|
50
50
|
export function redactLinks(body, fromPath, repo, who) {
|
|
51
|
-
|
|
51
|
+
// A route naming nothing fails closed where CommonMark reads a link (references.ts).
|
|
52
|
+
const links = linkPaths(body);
|
|
53
|
+
const hidden = (dest) => hidesPage(dest, fromPath, repo, who, false, links);
|
|
52
54
|
// Reference definitions first: remember which labels point at hidden pages.
|
|
53
55
|
const hiddenLabels = new Set();
|
|
56
|
+
// Only the definition CommonMark puts in effect for a label (code review, round 9): a
|
|
57
|
+
// `]:` inside a paragraph defines nothing, and is left for the sweep.
|
|
58
|
+
const defs = definitionsIn(body);
|
|
54
59
|
let out = body.replace(REF_DEF, (whole, label, dest) => {
|
|
55
60
|
if (!hidden(dest))
|
|
56
61
|
return whole;
|
|
62
|
+
const inEffect = defs?.get(normalLabel(label));
|
|
63
|
+
if (defs && (inEffect === undefined || !hidden(inEffect)))
|
|
64
|
+
return whole;
|
|
57
65
|
hiddenLabels.add(label.trim().toLowerCase());
|
|
58
66
|
return "";
|
|
59
67
|
});
|
|
60
68
|
if (hiddenLabels.size) {
|
|
61
69
|
// [text][label], [label][] and the shortcut form [label].
|
|
62
70
|
out = out.replace(/\[([^\][]{0,999})\]\[([^\][]{0,999})\]/g, (whole, text, label) => hiddenLabels.has((label || text).trim().toLowerCase()) ? RESTRICTED : whole);
|
|
63
|
-
|
|
71
|
+
// Not a label right after a `]`: that's a full reference whose text the form above
|
|
72
|
+
// couldn't read (it holds brackets: merge gate, PR 53, round 3), and the label alone
|
|
73
|
+
// would leave its text. The sweep takes those lines whole.
|
|
74
|
+
out = out.replace(/(?<!\])\[([^\][]{1,999})\](?![(\[:])/g, (whole, label) => (hiddenLabels.has(label.trim().toLowerCase()) ? RESTRICTED : whole));
|
|
64
75
|
}
|
|
65
76
|
out = out.replace(INLINE, (whole, _text, dest) => (hidden(dest) ? RESTRICTED : whole));
|
|
66
77
|
out = redactAnchors(out, (href) => hidesPage(href, fromPath, repo, who, true));
|
|
67
78
|
// The passes above keep the rest of a line, but read only the common forms. The sweep
|
|
68
79
|
// after them fails closed: a line that still names a hidden page, in any form a reader
|
|
69
80
|
// resolves, goes whole (references.ts).
|
|
70
|
-
|
|
81
|
+
// The labels the first pass took the definitions of: the sweep takes their references,
|
|
82
|
+
// which the forms above miss (`[ref]:` that can't start a definition is one).
|
|
83
|
+
return untilStable(out, redactionSweep(fromPath, repo, who, new Set(hiddenLabels)));
|
|
84
|
+
}
|
|
85
|
+
/** One pass of the sweep for `who` on page `fromPath`: `liberal`, every route naming nothing fails closed, a link or not. */
|
|
86
|
+
export function redactionSweep(fromPath, repo, who, gone = new Set()) {
|
|
87
|
+
const names = namesFor(repo, who);
|
|
88
|
+
// `gone`: the labels the first pass took the definitions of, whose references each pass
|
|
89
|
+
// takes too.
|
|
90
|
+
return (text, liberal) => replaceLines(text, namingLines(text, fromPath, names, 0, [], liberal, gone), RESTRICTED);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* `sweep` run until it changes nothing. Replacing a line can change what the rest means (a
|
|
94
|
+
* fence gone, code is markdown, and its links are links), so a pass can leave work for the
|
|
95
|
+
* next. Past `passes`, one last `liberal` pass, where every route naming nothing fails
|
|
96
|
+
* closed, ends it.
|
|
97
|
+
*/
|
|
98
|
+
export function untilStable(text, sweep, passes = 3) {
|
|
99
|
+
for (let pass = 1;; pass++) {
|
|
100
|
+
const next = sweep(text, pass > passes);
|
|
101
|
+
if (next === text || pass > passes)
|
|
102
|
+
return next;
|
|
103
|
+
text = next;
|
|
104
|
+
}
|
|
71
105
|
}
|
|
72
106
|
/** Each `<a href="…">…</a>` whose href is hidden, as the placeholder (its `</a>` within 4,000 characters). */
|
|
73
107
|
function redactAnchors(text, hidden) {
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type Config } from "./config.js";
|
|
2
|
+
import { UserError } from "./errors.js";
|
|
3
|
+
import { unprefixed } from "./links.js";
|
|
4
|
+
export { unprefixed };
|
|
5
|
+
export declare class AdoptError extends UserError {
|
|
6
|
+
}
|
|
7
|
+
export interface AdoptOptions {
|
|
8
|
+
now: Date;
|
|
9
|
+
/** The owning team where CODEOWNERS doesn't name exactly one. */
|
|
10
|
+
team?: string;
|
|
11
|
+
/** `review_every` for pages without one (default 90d). */
|
|
12
|
+
reviewEvery?: string;
|
|
13
|
+
/** Report, and write nothing. */
|
|
14
|
+
dryRun?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface AdoptResult {
|
|
17
|
+
dryRun: boolean;
|
|
18
|
+
/** Pages adopt changed (or would, on a dry run). */
|
|
19
|
+
adopted: string[];
|
|
20
|
+
/** Pages it had nothing to change on. */
|
|
21
|
+
unchanged: string[];
|
|
22
|
+
/** Pages, and symbolic links, it left as they are, and why. */
|
|
23
|
+
left: {
|
|
24
|
+
path: string;
|
|
25
|
+
reason: string;
|
|
26
|
+
}[];
|
|
27
|
+
/** What the team still has to do: a CODEOWNERS line, say. */
|
|
28
|
+
hints: string[];
|
|
29
|
+
/** Route links rewritten to relative `.md` links (R2). */
|
|
30
|
+
linksRewritten: number;
|
|
31
|
+
/** Links lint fails that adopt couldn't rewrite, as written, and why. */
|
|
32
|
+
linksLeft: {
|
|
33
|
+
path: string;
|
|
34
|
+
link: string;
|
|
35
|
+
why: string;
|
|
36
|
+
}[];
|
|
37
|
+
/** Keys added to the config's `frontmatter.extra_fields`: the adopted pages' unknown keys (R3). */
|
|
38
|
+
extraFields: string[];
|
|
39
|
+
}
|
|
40
|
+
/** The repo, its config and its knowledge root, by real path, each checked to be inside the repo (R9). */
|
|
41
|
+
export interface RepoPaths {
|
|
42
|
+
repo: string;
|
|
43
|
+
config: Config;
|
|
44
|
+
configPath: string;
|
|
45
|
+
knowledge: string;
|
|
46
|
+
}
|
|
47
|
+
/** Whether `child` is `parent` or under it, by path components (so `/repo-other` isn't under `/repo`). */
|
|
48
|
+
export declare function within(parent: string, child: string): boolean;
|
|
49
|
+
/** R9's checks, before any page is read: the config and the knowledge root are inside the repo. */
|
|
50
|
+
export declare function repoPaths(root: string, Err?: new (m: string) => Error, verb?: string): RepoPaths;
|
|
51
|
+
/**
|
|
52
|
+
* Why a file with other hard links is left, or null: the other path could be anywhere,
|
|
53
|
+
* outside the repo too, and a write here would change the file there (R9).
|
|
54
|
+
*/
|
|
55
|
+
export declare function hardLinked(abs: string, verb: string): string | null;
|
|
56
|
+
/** Why a symbolic link in the knowledge root is left. */
|
|
57
|
+
export declare const symlinkReason: (verb: string) => string;
|
|
58
|
+
/**
|
|
59
|
+
* The knowledge root's pages, as the loader finds them (directories starting with a dot
|
|
60
|
+
* skipped), by real path. It follows no symbolic link: the loader reads none, and a link is
|
|
61
|
+
* the one way a walk could leave the root (R9). A link that names a page or a directory is
|
|
62
|
+
* reported.
|
|
63
|
+
*/
|
|
64
|
+
export declare function walk(dir: string, out: {
|
|
65
|
+
files: string[];
|
|
66
|
+
symlinks: string[];
|
|
67
|
+
}): void;
|
|
68
|
+
export declare function adoptRepo(root: string, opts: AdoptOptions): AdoptResult;
|