@tangle-network/agent-docs 0.1.0 → 0.2.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 +14 -0
- package/dist/{chunk-EKQKRTAN.js → chunk-62CEZZVU.js} +163 -8
- package/dist/cli.js +36 -6
- package/dist/index.d.ts +38 -1
- package/dist/index.js +3 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -65,6 +65,20 @@ export default {
|
|
|
65
65
|
- **Private repos:** the deterministic core runs fully offline — no network, no LLM, no external service. This is the whole tool for a private repo.
|
|
66
66
|
- **Public repos:** add `--deepwiki` to pull the free, no-auth DeepWiki wiki + Q&A for the repo. If the repo isn't indexed yet, the augment is skipped and the deterministic map is unaffected. (Index a public repo once at `deepwiki.com/<owner>/<repo>`.)
|
|
67
67
|
|
|
68
|
+
## Filling the JSDoc gap (`suggest`)
|
|
69
|
+
|
|
70
|
+
The `llms.txt` / API pages are only as rich as your JSDoc — and most codebases leave the majority of exports undocumented. `agent-docs suggest` closes that gap with a cheap model, **at authoring time**:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
export TANGLE_API_KEY=... # any OpenAI-compatible endpoint via ROUTER_URL (default: Tangle router)
|
|
74
|
+
agent-docs suggest --subpath chat-routes # draft JSDoc for undocumented exports, write into source
|
|
75
|
+
agent-docs suggest --dry-run # draft + print, write nothing
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
It finds every exported declaration that has no JSDoc, drafts a one-line summary from the signature + source, and inserts it above the declaration. Review the diff, commit, then re-run `agent-docs` — the new comments flow into the gated `llms.txt` **deterministically**.
|
|
79
|
+
|
|
80
|
+
This is the tool's only LLM path, and it is deliberately at authoring time, not generation time: the model improves your **source comments**; the generated docs stay type-derived and never contain raw model text, so `--check` keeps working. Defaults to `gpt-4.1-mini` via the Tangle router; override with `--model`.
|
|
81
|
+
|
|
68
82
|
## Requirements
|
|
69
83
|
|
|
70
84
|
Node ≥ 20, and `typescript` resolvable from the target repo (it's a peer dependency — every TS repo has it). agent-docs carries no runtime dependencies of its own.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import { existsSync as existsSync4, mkdirSync, readdirSync as readdirSync2, readFileSync as
|
|
3
|
-
import { basename, dirname as dirname3, join as join5, resolve as
|
|
2
|
+
import { existsSync as existsSync4, mkdirSync, readdirSync as readdirSync2, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync2 } from "fs";
|
|
3
|
+
import { basename, dirname as dirname3, join as join5, resolve as resolve3 } from "path";
|
|
4
4
|
|
|
5
5
|
// src/config.ts
|
|
6
6
|
import { execFileSync } from "child_process";
|
|
@@ -478,6 +478,160 @@ async function loadTs(repoRoot) {
|
|
|
478
478
|
);
|
|
479
479
|
}
|
|
480
480
|
|
|
481
|
+
// src/suggest.ts
|
|
482
|
+
import { readFileSync as readFileSync4, writeFileSync } from "fs";
|
|
483
|
+
import { resolve as resolve2 } from "path";
|
|
484
|
+
function modelConfig(opts) {
|
|
485
|
+
const baseUrl = (opts.baseUrl ?? process.env.ROUTER_URL ?? "https://router.tangle.tools/v1").replace(/\/$/, "");
|
|
486
|
+
const apiKey = opts.apiKey ?? process.env.TANGLE_API_KEY ?? process.env.OPENAI_API_KEY ?? "";
|
|
487
|
+
const model = opts.model ?? "gpt-4.1-mini";
|
|
488
|
+
if (!apiKey) throw new Error("suggest: no API key. Set TANGLE_API_KEY (or pass --api-key).");
|
|
489
|
+
return { baseUrl, apiKey, model };
|
|
490
|
+
}
|
|
491
|
+
var PROMPT_RULES = 'Write ONE line of JSDoc summary for this TypeScript export. Rules: imperative mood ("Resolve\u2026", "Build\u2026"), describe what it IS or DOES, <= 110 characters, do NOT restate the type signature, do NOT start with "This function/type", no trailing period, no markdown. Return ONLY the summary text, nothing else.';
|
|
492
|
+
async function draftDoc(item, cfg) {
|
|
493
|
+
const content = `${PROMPT_RULES}
|
|
494
|
+
|
|
495
|
+
Name: ${item.name}
|
|
496
|
+
Kind: ${item.kind}
|
|
497
|
+
Signature: ${item.signature}
|
|
498
|
+
|
|
499
|
+
Source:
|
|
500
|
+
${item.snippet}`;
|
|
501
|
+
let res;
|
|
502
|
+
try {
|
|
503
|
+
res = await fetch(`${cfg.baseUrl}/chat/completions`, {
|
|
504
|
+
method: "POST",
|
|
505
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${cfg.apiKey}` },
|
|
506
|
+
body: JSON.stringify({
|
|
507
|
+
model: cfg.model,
|
|
508
|
+
messages: [{ role: "user", content }],
|
|
509
|
+
temperature: 0.2,
|
|
510
|
+
max_tokens: 120
|
|
511
|
+
})
|
|
512
|
+
});
|
|
513
|
+
} catch {
|
|
514
|
+
return null;
|
|
515
|
+
}
|
|
516
|
+
if (!res.ok) return null;
|
|
517
|
+
const j = await res.json();
|
|
518
|
+
const raw = j.choices?.[0]?.message?.content?.trim();
|
|
519
|
+
if (!raw) return null;
|
|
520
|
+
const line = raw.replace(/\s+/g, " ").replace(/^["'`]|["'`]$/g, "").replace(/^\/\*+|\*+\/$/g, "").replace(/\.$/, "").trim();
|
|
521
|
+
return line || null;
|
|
522
|
+
}
|
|
523
|
+
function statementOf(ts, decl) {
|
|
524
|
+
let n = decl;
|
|
525
|
+
while (n.parent && !ts.isSourceFile(n.parent)) n = n.parent;
|
|
526
|
+
return n;
|
|
527
|
+
}
|
|
528
|
+
function collectCandidates(ts, repoRoot, entries, subpath) {
|
|
529
|
+
const srcRoot = resolve2(repoRoot, "src");
|
|
530
|
+
const scoped = subpath ? entries.filter((e) => e.id === subpath) : entries;
|
|
531
|
+
const options = { noEmit: true, skipLibCheck: true, allowJs: true };
|
|
532
|
+
const program = ts.createProgram(
|
|
533
|
+
scoped.map((e) => e.file),
|
|
534
|
+
options
|
|
535
|
+
);
|
|
536
|
+
const checker = program.getTypeChecker();
|
|
537
|
+
const seen = /* @__PURE__ */ new Set();
|
|
538
|
+
const out = [];
|
|
539
|
+
for (const entry of scoped) {
|
|
540
|
+
const sf = program.getSourceFile(entry.file);
|
|
541
|
+
if (!sf) continue;
|
|
542
|
+
const moduleSym = checker.getSymbolAtLocation(sf);
|
|
543
|
+
if (!moduleSym) continue;
|
|
544
|
+
for (const exp of checker.getExportsOfModule(moduleSym)) {
|
|
545
|
+
const name = exp.getName();
|
|
546
|
+
if (name.startsWith("__")) continue;
|
|
547
|
+
let sym = exp;
|
|
548
|
+
if (sym.flags & ts.SymbolFlags.Alias) {
|
|
549
|
+
try {
|
|
550
|
+
sym = checker.getAliasedSymbol(sym);
|
|
551
|
+
} catch {
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
if (ts.displayPartsToString(exp.getDocumentationComment(checker)).trim()) continue;
|
|
555
|
+
if (ts.displayPartsToString(sym.getDocumentationComment(checker)).trim()) continue;
|
|
556
|
+
const decl = sym.declarations?.[0];
|
|
557
|
+
if (!decl) continue;
|
|
558
|
+
const declSf = decl.getSourceFile();
|
|
559
|
+
if (!declSf.fileName.startsWith(srcRoot + "/")) continue;
|
|
560
|
+
const stmt = statementOf(ts, decl);
|
|
561
|
+
const start = stmt.getStart(declSf);
|
|
562
|
+
const key = `${declSf.fileName}:${start}`;
|
|
563
|
+
if (seen.has(key)) continue;
|
|
564
|
+
seen.add(key);
|
|
565
|
+
const { line } = declSf.getLineAndCharacterOfPosition(start);
|
|
566
|
+
const lineStart = declSf.getPositionOfLineAndCharacter(line, 0);
|
|
567
|
+
const indent = (declSf.text.slice(lineStart, start).match(/^\s*/) ?? [""])[0];
|
|
568
|
+
const kind = kindOf(ts, sym);
|
|
569
|
+
const type = decl && sym.valueDeclaration ? checker.typeToString(checker.getTypeOfSymbolAtLocation(sym, sym.valueDeclaration)) : "";
|
|
570
|
+
const snippet = stmt.getText(declSf).split("\n").slice(0, 14).join("\n").slice(0, 1200);
|
|
571
|
+
out.push({ name, kind, signature: (type || kind).replace(/\s+/g, " ").slice(0, 200), file: declSf.fileName, pos: start, indent, snippet });
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
return out;
|
|
575
|
+
}
|
|
576
|
+
function kindOf(ts, sym) {
|
|
577
|
+
const f = sym.flags;
|
|
578
|
+
if (f & ts.SymbolFlags.Class) return "class";
|
|
579
|
+
if (f & ts.SymbolFlags.Interface) return "interface";
|
|
580
|
+
if (f & ts.SymbolFlags.TypeAlias) return "type";
|
|
581
|
+
if (f & ts.SymbolFlags.Enum) return "enum";
|
|
582
|
+
if (f & ts.SymbolFlags.Function) return "function";
|
|
583
|
+
return "const";
|
|
584
|
+
}
|
|
585
|
+
async function pool(items, n, fn) {
|
|
586
|
+
const q = [...items];
|
|
587
|
+
const workers = Array.from({ length: Math.min(n, q.length) }, async () => {
|
|
588
|
+
for (; ; ) {
|
|
589
|
+
const item = q.shift();
|
|
590
|
+
if (item === void 0) return;
|
|
591
|
+
await fn(item);
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
await Promise.all(workers);
|
|
595
|
+
}
|
|
596
|
+
async function suggest(repoRoot, opts = {}) {
|
|
597
|
+
const root = resolve2(repoRoot);
|
|
598
|
+
const cfg = modelConfig(opts);
|
|
599
|
+
const log = opts.log ?? (() => {
|
|
600
|
+
});
|
|
601
|
+
const config = await loadConfig(root);
|
|
602
|
+
const ts = await loadTs(root);
|
|
603
|
+
const entries = resolveEntries(root, config, ts);
|
|
604
|
+
let candidates = collectCandidates(ts, root, entries, opts.subpath);
|
|
605
|
+
if (opts.limit) candidates = candidates.slice(0, opts.limit);
|
|
606
|
+
log(`suggest: ${candidates.length} undocumented exports to draft (model ${cfg.model})`);
|
|
607
|
+
const drafts = [];
|
|
608
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
609
|
+
let done = 0;
|
|
610
|
+
await pool(candidates, opts.concurrency ?? 5, async (c) => {
|
|
611
|
+
const doc = await draftDoc(c, cfg);
|
|
612
|
+
done++;
|
|
613
|
+
if (done % 20 === 0) log(` drafted ${done}/${candidates.length}`);
|
|
614
|
+
if (!doc) return;
|
|
615
|
+
drafts.push({ name: c.name, file: c.file, doc });
|
|
616
|
+
const arr = byFile.get(c.file) ?? [];
|
|
617
|
+
arr.push({ pos: c.pos, indent: c.indent, doc });
|
|
618
|
+
byFile.set(c.file, arr);
|
|
619
|
+
});
|
|
620
|
+
const files = [];
|
|
621
|
+
if (!opts.dryRun) {
|
|
622
|
+
for (const [file, inserts] of byFile) {
|
|
623
|
+
let text = readFileSync4(file, "utf8");
|
|
624
|
+
for (const { pos, indent, doc } of inserts.sort((a, b) => b.pos - a.pos)) {
|
|
625
|
+
text = text.slice(0, pos) + `/** ${doc} */
|
|
626
|
+
${indent}` + text.slice(pos);
|
|
627
|
+
}
|
|
628
|
+
writeFileSync(file, text);
|
|
629
|
+
files.push(file);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
return { documented: drafts.length, skipped: candidates.length - drafts.length, files, drafts, dryRun: !!opts.dryRun };
|
|
633
|
+
}
|
|
634
|
+
|
|
481
635
|
// src/index.ts
|
|
482
636
|
var DEFAULT_ASK = [
|
|
483
637
|
"What is the high-level architecture and what are the main components?",
|
|
@@ -487,7 +641,7 @@ function readPackageDescription(root) {
|
|
|
487
641
|
const pkg = join5(root, "package.json");
|
|
488
642
|
if (!existsSync4(pkg)) return void 0;
|
|
489
643
|
try {
|
|
490
|
-
const d = JSON.parse(
|
|
644
|
+
const d = JSON.parse(readFileSync5(pkg, "utf8")).description;
|
|
491
645
|
return typeof d === "string" && d ? d : void 0;
|
|
492
646
|
} catch {
|
|
493
647
|
return void 0;
|
|
@@ -499,14 +653,14 @@ function describeSource(root, config) {
|
|
|
499
653
|
const pkg = join5(root, "package.json");
|
|
500
654
|
if (existsSync4(pkg)) {
|
|
501
655
|
try {
|
|
502
|
-
if (JSON.parse(
|
|
656
|
+
if (JSON.parse(readFileSync5(pkg, "utf8")).exports) return "package.json `exports`";
|
|
503
657
|
} catch {
|
|
504
658
|
}
|
|
505
659
|
}
|
|
506
660
|
return "src/index";
|
|
507
661
|
}
|
|
508
662
|
async function analyze(repoRoot, opts = {}) {
|
|
509
|
-
const root =
|
|
663
|
+
const root = resolve3(repoRoot);
|
|
510
664
|
const config = await loadConfig(root);
|
|
511
665
|
const ts = await loadTs(root);
|
|
512
666
|
const entries = resolveEntries(root, config, ts);
|
|
@@ -536,13 +690,13 @@ async function write(repoRoot, opts = {}) {
|
|
|
536
690
|
for (const [rel, content] of r.files) {
|
|
537
691
|
const abs = join5(r.root, rel);
|
|
538
692
|
mkdirSync(dirname3(abs), { recursive: true });
|
|
539
|
-
|
|
693
|
+
writeFileSync2(abs, content);
|
|
540
694
|
written.push(rel);
|
|
541
695
|
}
|
|
542
696
|
if (r.wiki) {
|
|
543
697
|
const abs = join5(r.root, r.wiki.path);
|
|
544
698
|
mkdirSync(dirname3(abs), { recursive: true });
|
|
545
|
-
|
|
699
|
+
writeFileSync2(abs, r.wiki.content);
|
|
546
700
|
written.push(r.wiki.path);
|
|
547
701
|
}
|
|
548
702
|
return { ...r, written };
|
|
@@ -554,7 +708,7 @@ async function check(repoRoot, opts = {}) {
|
|
|
554
708
|
for (const [rel, content] of r.files) {
|
|
555
709
|
const abs = join5(r.root, rel);
|
|
556
710
|
if (!existsSync4(abs)) missing.push(rel);
|
|
557
|
-
else if (
|
|
711
|
+
else if (readFileSync5(abs, "utf8") !== content) stale.push(rel);
|
|
558
712
|
}
|
|
559
713
|
const apiDir = join5(r.root, r.meta.out, "api");
|
|
560
714
|
const orphan = existsSync4(apiDir) ? readdirSync2(apiDir).filter((f) => f.endsWith(".md")).map((f) => `${r.meta.out}/api/${f}`).filter((rel) => !r.files.has(rel)) : [];
|
|
@@ -565,6 +719,7 @@ export {
|
|
|
565
719
|
parseRemoteUrl,
|
|
566
720
|
detectRepoSlug,
|
|
567
721
|
fetchDeepwiki,
|
|
722
|
+
suggest,
|
|
568
723
|
analyze,
|
|
569
724
|
write,
|
|
570
725
|
check
|
package/dist/cli.js
CHANGED
|
@@ -1,33 +1,46 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
check,
|
|
4
|
+
suggest,
|
|
4
5
|
write
|
|
5
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-62CEZZVU.js";
|
|
6
7
|
|
|
7
8
|
// src/cli.ts
|
|
8
9
|
var HELP = `agent-docs \u2014 deterministic repo surface + dependency map for any TypeScript package.
|
|
9
10
|
|
|
10
11
|
Usage:
|
|
11
|
-
agent-docs [gen] Regenerate docs/CODEMAP.md + docs/api/*.md +
|
|
12
|
+
agent-docs [gen] Regenerate docs/CODEMAP.md + docs/api/*.md + llms.txt + codemap.json
|
|
12
13
|
agent-docs --check Exit 1 if the committed deterministic docs are stale (CI gate)
|
|
13
14
|
agent-docs --deepwiki Also write docs/WIKI.md from DeepWiki (public GitHub repos only)
|
|
15
|
+
agent-docs suggest Draft JSDoc for UNDOCUMENTED exports via a cheap model, write into source
|
|
14
16
|
|
|
15
17
|
Options:
|
|
16
18
|
--repo <path> Repo root (default: cwd)
|
|
17
19
|
--out <dir> Output dir (default: docs, or agent-docs.config \`out\`)
|
|
20
|
+
suggest --subpath <id> Only document one subpath (e.g. chat-routes)
|
|
21
|
+
suggest --model <id> Model (default: claude-haiku-4-5-20251001, via TANGLE_API_KEY + ROUTER_URL)
|
|
22
|
+
suggest --limit <n> Cap exports documented this run
|
|
23
|
+
suggest --dry-run Draft + print, do not write to source
|
|
18
24
|
-h, --help
|
|
19
25
|
|
|
20
26
|
Entry points are auto-detected: agent-docs.config > tsup.config \`entry\` > package.json \`exports\` > src/index.
|
|
21
|
-
|
|
27
|
+
generate/check are deterministic and LLM-free; suggest is the only LLM path and runs at AUTHORING time \u2014 it edits
|
|
28
|
+
your JSDoc so the gated docs stay type-derived.`;
|
|
22
29
|
function parseArgs(argv) {
|
|
23
|
-
const args2 = { repo: process.cwd(),
|
|
30
|
+
const args2 = { command: "gen", repo: process.cwd(), deepwiki: false, dryRun: false, help: false };
|
|
24
31
|
const rest = argv.slice(2);
|
|
25
32
|
for (let i = 0; i < rest.length; i++) {
|
|
26
33
|
const a = rest[i];
|
|
27
|
-
if (a === "
|
|
34
|
+
if (a === "suggest") args2.command = "suggest";
|
|
35
|
+
else if (a === "--check" || a === "check") args2.command = "check";
|
|
28
36
|
else if (a === "--deepwiki") args2.deepwiki = true;
|
|
29
37
|
else if (a === "--repo") args2.repo = rest[++i];
|
|
30
38
|
else if (a === "--out") args2.out = rest[++i];
|
|
39
|
+
else if (a === "--subpath") args2.subpath = rest[++i];
|
|
40
|
+
else if (a === "--model") args2.model = rest[++i];
|
|
41
|
+
else if (a === "--limit") args2.limit = Number(rest[++i]);
|
|
42
|
+
else if (a === "--concurrency") args2.concurrency = Number(rest[++i]);
|
|
43
|
+
else if (a === "--dry-run") args2.dryRun = true;
|
|
31
44
|
else if (a === "-h" || a === "--help") args2.help = true;
|
|
32
45
|
else if (a === "gen") {
|
|
33
46
|
} else if (a.startsWith("-")) {
|
|
@@ -43,7 +56,7 @@ if (args.help) {
|
|
|
43
56
|
process.exit(0);
|
|
44
57
|
}
|
|
45
58
|
try {
|
|
46
|
-
if (args.check) {
|
|
59
|
+
if (args.command === "check") {
|
|
47
60
|
const r2 = await check(args.repo, { out: args.out });
|
|
48
61
|
if (r2.ok) {
|
|
49
62
|
console.log("agent-docs: docs are fresh");
|
|
@@ -55,6 +68,23 @@ try {
|
|
|
55
68
|
for (const f of r2.orphan) console.error(` orphaned: ${f}`);
|
|
56
69
|
process.exit(1);
|
|
57
70
|
}
|
|
71
|
+
if (args.command === "suggest") {
|
|
72
|
+
const r2 = await suggest(args.repo, {
|
|
73
|
+
subpath: args.subpath,
|
|
74
|
+
model: args.model,
|
|
75
|
+
limit: args.limit,
|
|
76
|
+
concurrency: args.concurrency,
|
|
77
|
+
dryRun: args.dryRun,
|
|
78
|
+
log: (m) => console.error(m)
|
|
79
|
+
});
|
|
80
|
+
if (r2.dryRun) {
|
|
81
|
+
for (const d of r2.drafts) console.log(`${d.name}: ${d.doc}`);
|
|
82
|
+
console.log(`agent-docs: drafted ${r2.documented} (dry run \u2014 nothing written)`);
|
|
83
|
+
} else {
|
|
84
|
+
console.log(`agent-docs: documented ${r2.documented} exports across ${r2.files.length} files (${r2.skipped} skipped). Review the diff, then \`agent-docs\` to refresh the map.`);
|
|
85
|
+
}
|
|
86
|
+
process.exit(0);
|
|
87
|
+
}
|
|
58
88
|
const r = await write(args.repo, { out: args.out, deepwiki: args.deepwiki });
|
|
59
89
|
const det = r.written.filter((f) => !f.endsWith("WIKI.md")).length;
|
|
60
90
|
console.log(`agent-docs: wrote ${det} files under ${r.meta.out}/ (${r.rows.length} entries)`);
|
package/dist/index.d.ts
CHANGED
|
@@ -107,6 +107,43 @@ declare function fetchDeepwiki(slug: string, { ask, timeoutMs }?: {
|
|
|
107
107
|
timeoutMs?: number;
|
|
108
108
|
}): Promise<DeepwikiResult | null>;
|
|
109
109
|
|
|
110
|
+
/**
|
|
111
|
+
* `agent-docs suggest` — draft a one-line JSDoc for every UNDOCUMENTED public
|
|
112
|
+
* export via a cheap model on an OpenAI-compatible endpoint (the Tangle router
|
|
113
|
+
* by default), and insert it into source above the declaration.
|
|
114
|
+
*
|
|
115
|
+
* This is the ONLY LLM-touching authoring path, and it is deliberately at
|
|
116
|
+
* AUTHORING time, not generation time: the model improves the source comments;
|
|
117
|
+
* once committed, the deterministic extractor picks them up so the gated
|
|
118
|
+
* `llms.txt` / `CODEMAP.md` stay type-derived and never contain raw model text.
|
|
119
|
+
* Nothing here runs during `generate` or `check`.
|
|
120
|
+
*/
|
|
121
|
+
interface SuggestOptions {
|
|
122
|
+
/** Only document this export subpath id (e.g. `chat-routes`); default: all. */
|
|
123
|
+
subpath?: string;
|
|
124
|
+
model?: string;
|
|
125
|
+
baseUrl?: string;
|
|
126
|
+
apiKey?: string;
|
|
127
|
+
/** Cap the number of exports documented in one run. */
|
|
128
|
+
limit?: number;
|
|
129
|
+
/** Draft + report, but do NOT write to source. */
|
|
130
|
+
dryRun?: boolean;
|
|
131
|
+
concurrency?: number;
|
|
132
|
+
log?: (msg: string) => void;
|
|
133
|
+
}
|
|
134
|
+
interface SuggestResult {
|
|
135
|
+
documented: number;
|
|
136
|
+
skipped: number;
|
|
137
|
+
files: string[];
|
|
138
|
+
drafts: Array<{
|
|
139
|
+
name: string;
|
|
140
|
+
file: string;
|
|
141
|
+
doc: string;
|
|
142
|
+
}>;
|
|
143
|
+
dryRun: boolean;
|
|
144
|
+
}
|
|
145
|
+
declare function suggest(repoRoot: string, opts?: SuggestOptions): Promise<SuggestResult>;
|
|
146
|
+
|
|
110
147
|
/** Extract the surface + graph (+ optional DeepWiki) and build the output files. */
|
|
111
148
|
declare function analyze(repoRoot: string, opts?: AnalyzeOptions): Promise<AnalyzeResult>;
|
|
112
149
|
/** Regenerate all files to disk. Cleans stale `api/*.md` first. */
|
|
@@ -120,4 +157,4 @@ declare function write(repoRoot: string, opts?: AnalyzeOptions): Promise<Analyze
|
|
|
120
157
|
*/
|
|
121
158
|
declare function check(repoRoot: string, opts?: AnalyzeOptions): Promise<CheckResult>;
|
|
122
159
|
|
|
123
|
-
export { type AnalyzeOptions, type AnalyzeResult, type CartographConfig, type CheckResult, type DeepwikiResult, type Entry, type ExportInfo, type ExportKind, type Meta, type RepoSlug, type Row, type TS, analyze, check, detectRepoSlug, fetchDeepwiki, parseRemoteUrl, write };
|
|
160
|
+
export { type AnalyzeOptions, type AnalyzeResult, type CartographConfig, type CheckResult, type DeepwikiResult, type Entry, type ExportInfo, type ExportKind, type Meta, type RepoSlug, type Row, type SuggestOptions, type SuggestResult, type TS, analyze, check, detectRepoSlug, fetchDeepwiki, parseRemoteUrl, suggest, write };
|
package/dist/index.js
CHANGED
|
@@ -4,13 +4,15 @@ import {
|
|
|
4
4
|
detectRepoSlug,
|
|
5
5
|
fetchDeepwiki,
|
|
6
6
|
parseRemoteUrl,
|
|
7
|
+
suggest,
|
|
7
8
|
write
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-62CEZZVU.js";
|
|
9
10
|
export {
|
|
10
11
|
analyze,
|
|
11
12
|
check,
|
|
12
13
|
detectRepoSlug,
|
|
13
14
|
fetchDeepwiki,
|
|
14
15
|
parseRemoteUrl,
|
|
16
|
+
suggest,
|
|
15
17
|
write
|
|
16
18
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-docs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Deterministic repo surface + dependency map for any TypeScript package, with an optional DeepWiki narrative augment for public repos.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -30,7 +30,9 @@
|
|
|
30
30
|
"typescript": ">=5.0.0"
|
|
31
31
|
},
|
|
32
32
|
"peerDependenciesMeta": {
|
|
33
|
-
"typescript": {
|
|
33
|
+
"typescript": {
|
|
34
|
+
"optional": false
|
|
35
|
+
}
|
|
34
36
|
},
|
|
35
37
|
"devDependencies": {
|
|
36
38
|
"@types/node": "^22.0.0",
|