docbridge 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +111 -1
- package/README.md +55 -372
- package/dist/bin/darwin-arm64/SHA256SUMS +2 -2
- package/dist/bin/darwin-arm64/docbridge-swift-scanner +0 -0
- package/dist/bin/darwin-arm64/docbridge_dart_scanner +0 -0
- package/dist/bin/linux-x64/SHA256SUMS +2 -2
- package/dist/bin/linux-x64/docbridge-swift-scanner +1 -1
- package/dist/bin/linux-x64/docbridge_dart_scanner +0 -0
- package/dist/index.js +2502 -947
- package/docs/README.md +66 -0
- package/docs/user/automation.md +104 -0
- package/docs/user/commands.md +75 -4
- package/docs/user/configuration.md +13 -3
- package/docs/user/getting-started.md +6 -2
- package/docs/user/linking.md +202 -0
- package/docs/user/troubleshooting.md +62 -22
- package/package.json +2 -1
- package/schemas/docbridge.links.schema.json +39 -0
- package/schemas/scanner-worker.schema.json +1 -0
- package/templates/skills/README.md +16 -23
- package/templates/skills/docbridge/SKILL.md +125 -0
- package/docs/user/agent-integration.md +0 -70
- package/docs/user/annotations.md +0 -52
- package/templates/skills/docbridge-adopt/SKILL.md +0 -96
- package/templates/skills/docbridge-annotate/SKILL.md +0 -99
- package/templates/skills/docbridge-link/SKILL.md +0 -80
- package/templates/skills/docbridge-review/SKILL.md +0 -62
- package/templates/skills/docbridge-sync/SKILL.md +0 -68
package/dist/index.js
CHANGED
|
@@ -411,11 +411,11 @@ var require_codegen = __commonJS((exports) => {
|
|
|
411
411
|
const rhs = this.rhs === undefined ? "" : ` = ${this.rhs}`;
|
|
412
412
|
return `${varKind} ${this.name}${rhs};` + _n;
|
|
413
413
|
}
|
|
414
|
-
optimizeNames(names,
|
|
414
|
+
optimizeNames(names, constants2) {
|
|
415
415
|
if (!names[this.name.str])
|
|
416
416
|
return;
|
|
417
417
|
if (this.rhs)
|
|
418
|
-
this.rhs = optimizeExpr(this.rhs, names,
|
|
418
|
+
this.rhs = optimizeExpr(this.rhs, names, constants2);
|
|
419
419
|
return this;
|
|
420
420
|
}
|
|
421
421
|
get names() {
|
|
@@ -433,10 +433,10 @@ var require_codegen = __commonJS((exports) => {
|
|
|
433
433
|
render({ _n }) {
|
|
434
434
|
return `${this.lhs} = ${this.rhs};` + _n;
|
|
435
435
|
}
|
|
436
|
-
optimizeNames(names,
|
|
436
|
+
optimizeNames(names, constants2) {
|
|
437
437
|
if (this.lhs instanceof code_1.Name && !names[this.lhs.str] && !this.sideEffects)
|
|
438
438
|
return;
|
|
439
|
-
this.rhs = optimizeExpr(this.rhs, names,
|
|
439
|
+
this.rhs = optimizeExpr(this.rhs, names, constants2);
|
|
440
440
|
return this;
|
|
441
441
|
}
|
|
442
442
|
get names() {
|
|
@@ -502,8 +502,8 @@ var require_codegen = __commonJS((exports) => {
|
|
|
502
502
|
optimizeNodes() {
|
|
503
503
|
return `${this.code}` ? this : undefined;
|
|
504
504
|
}
|
|
505
|
-
optimizeNames(names,
|
|
506
|
-
this.code = optimizeExpr(this.code, names,
|
|
505
|
+
optimizeNames(names, constants2) {
|
|
506
|
+
this.code = optimizeExpr(this.code, names, constants2);
|
|
507
507
|
return this;
|
|
508
508
|
}
|
|
509
509
|
get names() {
|
|
@@ -533,12 +533,12 @@ var require_codegen = __commonJS((exports) => {
|
|
|
533
533
|
}
|
|
534
534
|
return nodes.length > 0 ? this : undefined;
|
|
535
535
|
}
|
|
536
|
-
optimizeNames(names,
|
|
536
|
+
optimizeNames(names, constants2) {
|
|
537
537
|
const { nodes } = this;
|
|
538
538
|
let i = nodes.length;
|
|
539
539
|
while (i--) {
|
|
540
540
|
const n = nodes[i];
|
|
541
|
-
if (n.optimizeNames(names,
|
|
541
|
+
if (n.optimizeNames(names, constants2))
|
|
542
542
|
continue;
|
|
543
543
|
subtractNames(names, n.names);
|
|
544
544
|
nodes.splice(i, 1);
|
|
@@ -595,12 +595,12 @@ var require_codegen = __commonJS((exports) => {
|
|
|
595
595
|
return;
|
|
596
596
|
return this;
|
|
597
597
|
}
|
|
598
|
-
optimizeNames(names,
|
|
598
|
+
optimizeNames(names, constants2) {
|
|
599
599
|
var _a;
|
|
600
|
-
this.else = (_a = this.else) === null || _a === undefined ? undefined : _a.optimizeNames(names,
|
|
601
|
-
if (!(super.optimizeNames(names,
|
|
600
|
+
this.else = (_a = this.else) === null || _a === undefined ? undefined : _a.optimizeNames(names, constants2);
|
|
601
|
+
if (!(super.optimizeNames(names, constants2) || this.else))
|
|
602
602
|
return;
|
|
603
|
-
this.condition = optimizeExpr(this.condition, names,
|
|
603
|
+
this.condition = optimizeExpr(this.condition, names, constants2);
|
|
604
604
|
return this;
|
|
605
605
|
}
|
|
606
606
|
get names() {
|
|
@@ -625,10 +625,10 @@ var require_codegen = __commonJS((exports) => {
|
|
|
625
625
|
render(opts) {
|
|
626
626
|
return `for(${this.iteration})` + super.render(opts);
|
|
627
627
|
}
|
|
628
|
-
optimizeNames(names,
|
|
629
|
-
if (!super.optimizeNames(names,
|
|
628
|
+
optimizeNames(names, constants2) {
|
|
629
|
+
if (!super.optimizeNames(names, constants2))
|
|
630
630
|
return;
|
|
631
|
-
this.iteration = optimizeExpr(this.iteration, names,
|
|
631
|
+
this.iteration = optimizeExpr(this.iteration, names, constants2);
|
|
632
632
|
return this;
|
|
633
633
|
}
|
|
634
634
|
get names() {
|
|
@@ -666,10 +666,10 @@ var require_codegen = __commonJS((exports) => {
|
|
|
666
666
|
render(opts) {
|
|
667
667
|
return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts);
|
|
668
668
|
}
|
|
669
|
-
optimizeNames(names,
|
|
670
|
-
if (!super.optimizeNames(names,
|
|
669
|
+
optimizeNames(names, constants2) {
|
|
670
|
+
if (!super.optimizeNames(names, constants2))
|
|
671
671
|
return;
|
|
672
|
-
this.iterable = optimizeExpr(this.iterable, names,
|
|
672
|
+
this.iterable = optimizeExpr(this.iterable, names, constants2);
|
|
673
673
|
return this;
|
|
674
674
|
}
|
|
675
675
|
get names() {
|
|
@@ -714,11 +714,11 @@ var require_codegen = __commonJS((exports) => {
|
|
|
714
714
|
(_b = this.finally) === null || _b === undefined || _b.optimizeNodes();
|
|
715
715
|
return this;
|
|
716
716
|
}
|
|
717
|
-
optimizeNames(names,
|
|
717
|
+
optimizeNames(names, constants2) {
|
|
718
718
|
var _a, _b;
|
|
719
|
-
super.optimizeNames(names,
|
|
720
|
-
(_a = this.catch) === null || _a === undefined || _a.optimizeNames(names,
|
|
721
|
-
(_b = this.finally) === null || _b === undefined || _b.optimizeNames(names,
|
|
719
|
+
super.optimizeNames(names, constants2);
|
|
720
|
+
(_a = this.catch) === null || _a === undefined || _a.optimizeNames(names, constants2);
|
|
721
|
+
(_b = this.finally) === null || _b === undefined || _b.optimizeNames(names, constants2);
|
|
722
722
|
return this;
|
|
723
723
|
}
|
|
724
724
|
get names() {
|
|
@@ -992,7 +992,7 @@ var require_codegen = __commonJS((exports) => {
|
|
|
992
992
|
function addExprNames(names, from) {
|
|
993
993
|
return from instanceof code_1._CodeOrName ? addNames(names, from.names) : names;
|
|
994
994
|
}
|
|
995
|
-
function optimizeExpr(expr, names,
|
|
995
|
+
function optimizeExpr(expr, names, constants2) {
|
|
996
996
|
if (expr instanceof code_1.Name)
|
|
997
997
|
return replaceName(expr);
|
|
998
998
|
if (!canOptimize(expr))
|
|
@@ -1007,14 +1007,14 @@ var require_codegen = __commonJS((exports) => {
|
|
|
1007
1007
|
return items;
|
|
1008
1008
|
}, []));
|
|
1009
1009
|
function replaceName(n) {
|
|
1010
|
-
const c =
|
|
1010
|
+
const c = constants2[n.str];
|
|
1011
1011
|
if (c === undefined || names[n.str] !== 1)
|
|
1012
1012
|
return n;
|
|
1013
1013
|
delete names[n.str];
|
|
1014
1014
|
return c;
|
|
1015
1015
|
}
|
|
1016
1016
|
function canOptimize(e) {
|
|
1017
|
-
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 &&
|
|
1017
|
+
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants2[c.str] !== undefined);
|
|
1018
1018
|
}
|
|
1019
1019
|
}
|
|
1020
1020
|
function subtractNames(names, from) {
|
|
@@ -2919,7 +2919,7 @@ var require_compile = __commonJS((exports) => {
|
|
|
2919
2919
|
const schOrFunc = root.refs[ref];
|
|
2920
2920
|
if (schOrFunc)
|
|
2921
2921
|
return schOrFunc;
|
|
2922
|
-
let _sch =
|
|
2922
|
+
let _sch = resolve2.call(this, root, ref);
|
|
2923
2923
|
if (_sch === undefined) {
|
|
2924
2924
|
const schema = (_a = root.localRefs) === null || _a === undefined ? undefined : _a[ref];
|
|
2925
2925
|
const { schemaId } = this.opts;
|
|
@@ -2946,7 +2946,7 @@ var require_compile = __commonJS((exports) => {
|
|
|
2946
2946
|
function sameSchemaEnv(s1, s2) {
|
|
2947
2947
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
2948
2948
|
}
|
|
2949
|
-
function
|
|
2949
|
+
function resolve2(root, ref) {
|
|
2950
2950
|
let sch;
|
|
2951
2951
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
2952
2952
|
ref = sch;
|
|
@@ -3532,7 +3532,7 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
3532
3532
|
}
|
|
3533
3533
|
return uri;
|
|
3534
3534
|
}
|
|
3535
|
-
function
|
|
3535
|
+
function resolve2(baseURI, relativeURI, options) {
|
|
3536
3536
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3537
3537
|
const { parsed: baseParsed, malformedAuthorityOrPort: baseMalformed } = parseWithStatus(baseURI, schemelessOptions);
|
|
3538
3538
|
const { parsed: relativeParsed, malformedAuthorityOrPort: relativeMalformed } = parseWithStatus(relativeURI, schemelessOptions);
|
|
@@ -3817,7 +3817,7 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
3817
3817
|
var fastUri = {
|
|
3818
3818
|
SCHEMES,
|
|
3819
3819
|
normalize,
|
|
3820
|
-
resolve,
|
|
3820
|
+
resolve: resolve2,
|
|
3821
3821
|
resolveComponent,
|
|
3822
3822
|
equal,
|
|
3823
3823
|
serialize,
|
|
@@ -7155,7 +7155,7 @@ var require_typescript = __commonJS((exports, module) => {
|
|
|
7155
7155
|
compact: () => compact,
|
|
7156
7156
|
compareBooleans: () => compareBooleans,
|
|
7157
7157
|
compareDataObjects: () => compareDataObjects,
|
|
7158
|
-
compareDiagnostics: () =>
|
|
7158
|
+
compareDiagnostics: () => compareDiagnostics2,
|
|
7159
7159
|
compareEmitHelpers: () => compareEmitHelpers,
|
|
7160
7160
|
compareNumberOfDirectorySeparators: () => compareNumberOfDirectorySeparators,
|
|
7161
7161
|
comparePaths: () => comparePaths2,
|
|
@@ -7474,7 +7474,7 @@ var require_typescript = __commonJS((exports, module) => {
|
|
|
7474
7474
|
forEachUnique: () => forEachUnique,
|
|
7475
7475
|
forEachYieldExpression: () => forEachYieldExpression,
|
|
7476
7476
|
formatColorAndReset: () => formatColorAndReset,
|
|
7477
|
-
formatDiagnostic: () =>
|
|
7477
|
+
formatDiagnostic: () => formatDiagnostic2,
|
|
7478
7478
|
formatDiagnostics: () => formatDiagnostics,
|
|
7479
7479
|
formatDiagnosticsWithColorAndContext: () => formatDiagnosticsWithColorAndContext,
|
|
7480
7480
|
formatGeneratedName: () => formatGeneratedName,
|
|
@@ -14966,7 +14966,7 @@ ${lanes.join(`
|
|
|
14966
14966
|
return process.memoryUsage().heapUsed;
|
|
14967
14967
|
},
|
|
14968
14968
|
getFileSize(path) {
|
|
14969
|
-
const stat =
|
|
14969
|
+
const stat = statSync2(path);
|
|
14970
14970
|
if (stat == null ? undefined : stat.isFile()) {
|
|
14971
14971
|
return stat.size;
|
|
14972
14972
|
}
|
|
@@ -15009,7 +15009,7 @@ ${lanes.join(`
|
|
|
15009
15009
|
}
|
|
15010
15010
|
};
|
|
15011
15011
|
return nodeSystem;
|
|
15012
|
-
function
|
|
15012
|
+
function statSync2(path) {
|
|
15013
15013
|
try {
|
|
15014
15014
|
return _fs.statSync(path, statSyncOptions);
|
|
15015
15015
|
} catch {
|
|
@@ -15061,7 +15061,7 @@ ${lanes.join(`
|
|
|
15061
15061
|
activeSession.post("Profiler.stop", (err, { profile }) => {
|
|
15062
15062
|
var _a;
|
|
15063
15063
|
if (!err) {
|
|
15064
|
-
if ((_a =
|
|
15064
|
+
if ((_a = statSync2(profilePath)) == null ? undefined : _a.isDirectory()) {
|
|
15065
15065
|
profilePath = _path.join(profilePath, `${(/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-")}+P${process.pid}.cpuprofile`);
|
|
15066
15066
|
}
|
|
15067
15067
|
try {
|
|
@@ -15170,7 +15170,7 @@ ${lanes.join(`
|
|
|
15170
15170
|
let stat;
|
|
15171
15171
|
if (typeof dirent === "string" || dirent.isSymbolicLink()) {
|
|
15172
15172
|
const name = combinePaths(path, entry);
|
|
15173
|
-
stat =
|
|
15173
|
+
stat = statSync2(name);
|
|
15174
15174
|
if (!stat) {
|
|
15175
15175
|
continue;
|
|
15176
15176
|
}
|
|
@@ -15194,7 +15194,7 @@ ${lanes.join(`
|
|
|
15194
15194
|
return matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames2, process.cwd(), depth, getAccessibleFileSystemEntries, realpath);
|
|
15195
15195
|
}
|
|
15196
15196
|
function fileSystemEntryExists(path, entryKind) {
|
|
15197
|
-
const stat =
|
|
15197
|
+
const stat = statSync2(path);
|
|
15198
15198
|
if (!stat) {
|
|
15199
15199
|
return false;
|
|
15200
15200
|
}
|
|
@@ -15228,7 +15228,7 @@ ${lanes.join(`
|
|
|
15228
15228
|
}
|
|
15229
15229
|
function getModifiedTime3(path) {
|
|
15230
15230
|
var _a;
|
|
15231
|
-
return (_a =
|
|
15231
|
+
return (_a = statSync2(path)) == null ? undefined : _a.mtime;
|
|
15232
15232
|
}
|
|
15233
15233
|
function setModifiedTime(path, time) {
|
|
15234
15234
|
try {
|
|
@@ -20956,7 +20956,7 @@ ${lanes.join(`
|
|
|
20956
20956
|
return pathIsRelative(moduleName) || isRootedDiskPath(moduleName);
|
|
20957
20957
|
}
|
|
20958
20958
|
function sortAndDeduplicateDiagnostics(diagnostics) {
|
|
20959
|
-
return sortAndDeduplicate(diagnostics,
|
|
20959
|
+
return sortAndDeduplicate(diagnostics, compareDiagnostics2, diagnosticsEqualityComparer);
|
|
20960
20960
|
}
|
|
20961
20961
|
var targetToLibMap = /* @__PURE__ */ new Map([
|
|
20962
20962
|
[99, "lib.esnext.full.d.ts"],
|
|
@@ -27716,7 +27716,7 @@ ${lanes.join(`
|
|
|
27716
27716
|
function getDiagnosticFilePath(diagnostic) {
|
|
27717
27717
|
return diagnostic.file ? diagnostic.file.path : undefined;
|
|
27718
27718
|
}
|
|
27719
|
-
function
|
|
27719
|
+
function compareDiagnostics2(d1, d2) {
|
|
27720
27720
|
return compareDiagnosticsSkipRelatedInformation(d1, d2) || compareRelatedInformation(d1, d2) || 0;
|
|
27721
27721
|
}
|
|
27722
27722
|
function compareDiagnosticsSkipRelatedInformation(d1, d2) {
|
|
@@ -27731,7 +27731,7 @@ ${lanes.join(`
|
|
|
27731
27731
|
if (d1.relatedInformation && d2.relatedInformation) {
|
|
27732
27732
|
return compareValues(d2.relatedInformation.length, d1.relatedInformation.length) || forEach(d1.relatedInformation, (d1i, index) => {
|
|
27733
27733
|
const d2i = d2.relatedInformation[index];
|
|
27734
|
-
return
|
|
27734
|
+
return compareDiagnostics2(d1i, d2i);
|
|
27735
27735
|
}) || 0;
|
|
27736
27736
|
}
|
|
27737
27737
|
return d1.relatedInformation ? -1 : 1;
|
|
@@ -54786,7 +54786,7 @@ ${lanes.join(`
|
|
|
54786
54786
|
err.relatedInformation = err.relatedInformation || [];
|
|
54787
54787
|
const leadingMessage = createDiagnosticForNode(adjustedNode, Diagnostics._0_was_also_declared_here, symbolName2);
|
|
54788
54788
|
const followOnMessage = createDiagnosticForNode(adjustedNode, Diagnostics.and_here);
|
|
54789
|
-
if (length(err.relatedInformation) >= 5 || some(err.relatedInformation, (r) =>
|
|
54789
|
+
if (length(err.relatedInformation) >= 5 || some(err.relatedInformation, (r) => compareDiagnostics2(r, followOnMessage) === 0 || compareDiagnostics2(r, leadingMessage) === 0))
|
|
54790
54790
|
continue;
|
|
54791
54791
|
addRelatedInfo(err, !length(err.relatedInformation) ? leadingMessage : followOnMessage);
|
|
54792
54792
|
}
|
|
@@ -87995,7 +87995,7 @@ ${lanes.join(`
|
|
|
87995
87995
|
}
|
|
87996
87996
|
const currentGlobalDiagnostics = diagnostics.getGlobalDiagnostics();
|
|
87997
87997
|
if (currentGlobalDiagnostics !== previousGlobalDiagnostics) {
|
|
87998
|
-
const deferredGlobalDiagnostics = relativeComplement(previousGlobalDiagnostics, currentGlobalDiagnostics,
|
|
87998
|
+
const deferredGlobalDiagnostics = relativeComplement(previousGlobalDiagnostics, currentGlobalDiagnostics, compareDiagnostics2);
|
|
87999
87999
|
return concatenate(deferredGlobalDiagnostics, semanticDiagnostics);
|
|
88000
88000
|
} else if (previousGlobalDiagnosticsSize === 0 && currentGlobalDiagnostics.length > 0) {
|
|
88001
88001
|
return concatenate(currentGlobalDiagnostics, semanticDiagnostics);
|
|
@@ -105133,14 +105133,14 @@ ${lanes.join(`
|
|
|
105133
105133
|
}
|
|
105134
105134
|
}
|
|
105135
105135
|
function createImportCallExpressionAMD(arg, containsLexicalThis) {
|
|
105136
|
-
const
|
|
105136
|
+
const resolve2 = factory2.createUniqueName("resolve");
|
|
105137
105137
|
const reject = factory2.createUniqueName("reject");
|
|
105138
105138
|
const parameters = [
|
|
105139
|
-
factory2.createParameterDeclaration(undefined, undefined,
|
|
105139
|
+
factory2.createParameterDeclaration(undefined, undefined, resolve2),
|
|
105140
105140
|
factory2.createParameterDeclaration(undefined, undefined, reject)
|
|
105141
105141
|
];
|
|
105142
105142
|
const body = factory2.createBlock([
|
|
105143
|
-
factory2.createExpressionStatement(factory2.createCallExpression(factory2.createIdentifier("require"), undefined, [factory2.createArrayLiteralExpression([arg || factory2.createOmittedExpression()]),
|
|
105143
|
+
factory2.createExpressionStatement(factory2.createCallExpression(factory2.createIdentifier("require"), undefined, [factory2.createArrayLiteralExpression([arg || factory2.createOmittedExpression()]), resolve2, reject]))
|
|
105144
105144
|
]);
|
|
105145
105145
|
let func;
|
|
105146
105146
|
if (languageVersion >= 2) {
|
|
@@ -113910,11 +113910,11 @@ ${lanes.join(`
|
|
|
113910
113910
|
function formatDiagnostics(diagnostics, host) {
|
|
113911
113911
|
let output = "";
|
|
113912
113912
|
for (const diagnostic of diagnostics) {
|
|
113913
|
-
output +=
|
|
113913
|
+
output += formatDiagnostic2(diagnostic, host);
|
|
113914
113914
|
}
|
|
113915
113915
|
return output;
|
|
113916
113916
|
}
|
|
113917
|
-
function
|
|
113917
|
+
function formatDiagnostic2(diagnostic, host) {
|
|
113918
113918
|
const errorMessage = `${diagnosticCategoryName(diagnostic)} TS${diagnostic.code}: ${flattenDiagnosticMessageText(diagnostic.messageText, host.getNewLine())}${host.getNewLine()}`;
|
|
113919
113919
|
if (diagnostic.file) {
|
|
113920
113920
|
const { line, character } = getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start);
|
|
@@ -119897,7 +119897,7 @@ ${lanes.join(`
|
|
|
119897
119897
|
getCanonicalFileName: createGetCanonicalFileName(system.useCaseSensitiveFileNames)
|
|
119898
119898
|
};
|
|
119899
119899
|
if (!pretty) {
|
|
119900
|
-
return (diagnostic) => system.write(
|
|
119900
|
+
return (diagnostic) => system.write(formatDiagnostic2(diagnostic, host));
|
|
119901
119901
|
}
|
|
119902
119902
|
const diagnostics = new Array(1);
|
|
119903
119903
|
return (diagnostic) => {
|
|
@@ -163155,7 +163155,7 @@ ${options.prefix}` : `
|
|
|
163155
163155
|
compact: () => compact,
|
|
163156
163156
|
compareBooleans: () => compareBooleans,
|
|
163157
163157
|
compareDataObjects: () => compareDataObjects,
|
|
163158
|
-
compareDiagnostics: () =>
|
|
163158
|
+
compareDiagnostics: () => compareDiagnostics2,
|
|
163159
163159
|
compareEmitHelpers: () => compareEmitHelpers,
|
|
163160
163160
|
compareNumberOfDirectorySeparators: () => compareNumberOfDirectorySeparators,
|
|
163161
163161
|
comparePaths: () => comparePaths2,
|
|
@@ -163474,7 +163474,7 @@ ${options.prefix}` : `
|
|
|
163474
163474
|
forEachUnique: () => forEachUnique,
|
|
163475
163475
|
forEachYieldExpression: () => forEachYieldExpression,
|
|
163476
163476
|
formatColorAndReset: () => formatColorAndReset,
|
|
163477
|
-
formatDiagnostic: () =>
|
|
163477
|
+
formatDiagnostic: () => formatDiagnostic2,
|
|
163478
163478
|
formatDiagnostics: () => formatDiagnostics,
|
|
163479
163479
|
formatDiagnosticsWithColorAndContext: () => formatDiagnosticsWithColorAndContext,
|
|
163480
163480
|
formatGeneratedName: () => formatGeneratedName,
|
|
@@ -175315,8 +175315,8 @@ Additional information: BADCLIENT: Bad error code, ${badCode} not found in range
|
|
|
175315
175315
|
installPackage(options) {
|
|
175316
175316
|
this.packageInstallId++;
|
|
175317
175317
|
const request = { kind: "installPackage", ...options, id: this.packageInstallId };
|
|
175318
|
-
const promise = new Promise((
|
|
175319
|
-
(this.packageInstalledPromise ?? (this.packageInstalledPromise = /* @__PURE__ */ new Map)).set(this.packageInstallId, { resolve, reject });
|
|
175318
|
+
const promise = new Promise((resolve2, reject) => {
|
|
175319
|
+
(this.packageInstalledPromise ?? (this.packageInstalledPromise = /* @__PURE__ */ new Map)).set(this.packageInstallId, { resolve: resolve2, reject });
|
|
175320
175320
|
});
|
|
175321
175321
|
this.installer.send(request);
|
|
175322
175322
|
return promise;
|
|
@@ -175576,12 +175576,12 @@ Additional information: BADCLIENT: Bad error code, ${badCode} not found in range
|
|
|
175576
175576
|
});
|
|
175577
175577
|
|
|
175578
175578
|
// src/cli/index.ts
|
|
175579
|
-
import { existsSync as
|
|
175580
|
-
import { join as
|
|
175579
|
+
import { existsSync as existsSync6, readFileSync as readFileSync9, statSync as statSync5 } from "node:fs";
|
|
175580
|
+
import { join as join14, resolve as resolve5 } from "node:path";
|
|
175581
175581
|
// package.json
|
|
175582
175582
|
var package_default = {
|
|
175583
175583
|
name: "docbridge",
|
|
175584
|
-
version: "0.
|
|
175584
|
+
version: "0.9.0",
|
|
175585
175585
|
description: "A CLI that links Markdown specifications and code declarations. Runs on Node.js and Bun.",
|
|
175586
175586
|
keywords: [
|
|
175587
175587
|
"bun",
|
|
@@ -175624,6 +175624,7 @@ var package_default = {
|
|
|
175624
175624
|
devDependencies: {
|
|
175625
175625
|
"@types/bun": "^1.3.14",
|
|
175626
175626
|
ajv: "^8.20.0",
|
|
175627
|
+
"markdown-it": "15.0.1",
|
|
175627
175628
|
oxfmt: "0.57.0",
|
|
175628
175629
|
oxlint: "1.73.0",
|
|
175629
175630
|
typescript: "^5.9.0"
|
|
@@ -175636,20 +175637,7 @@ var package_default = {
|
|
|
175636
175637
|
|
|
175637
175638
|
// src/core/config.ts
|
|
175638
175639
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
175639
|
-
import { join as
|
|
175640
|
-
|
|
175641
|
-
// src/core/code-language.ts
|
|
175642
|
-
import { accessSync, chmodSync, constants, existsSync, realpathSync, statSync } from "node:fs";
|
|
175643
|
-
import { dirname as dirname2, join as join3, resolve } from "node:path";
|
|
175644
|
-
import { fileURLToPath } from "node:url";
|
|
175645
|
-
|
|
175646
|
-
// src/core/code-adapter-registry.ts
|
|
175647
|
-
var codeAdapters = {};
|
|
175648
|
-
|
|
175649
|
-
// src/core/error.ts
|
|
175650
|
-
function reasonOf(error) {
|
|
175651
|
-
return error instanceof Error ? error.message : String(error);
|
|
175652
|
-
}
|
|
175640
|
+
import { join as join2 } from "node:path";
|
|
175653
175641
|
|
|
175654
175642
|
// src/core/glob.ts
|
|
175655
175643
|
import { lstatSync, readFileSync, readdirSync } from "node:fs";
|
|
@@ -175802,12 +175790,562 @@ function escapeRegExpChar(char) {
|
|
|
175802
175790
|
return /[.*+?^${}()|[\]\\]/.test(char) ? `\\${char}` : char;
|
|
175803
175791
|
}
|
|
175804
175792
|
|
|
175793
|
+
// src/core/code-language.ts
|
|
175794
|
+
var KNOWN_CODE_LANGUAGES = [
|
|
175795
|
+
"typescript",
|
|
175796
|
+
"swift",
|
|
175797
|
+
"dart",
|
|
175798
|
+
"rust"
|
|
175799
|
+
];
|
|
175800
|
+
function isCodeLanguage(value) {
|
|
175801
|
+
return KNOWN_CODE_LANGUAGES.includes(value);
|
|
175802
|
+
}
|
|
175803
|
+
function collectCodeFiles(projectRoot, codeInclude) {
|
|
175804
|
+
const collected = [];
|
|
175805
|
+
const seen = new Set;
|
|
175806
|
+
for (const language of KNOWN_CODE_LANGUAGES) {
|
|
175807
|
+
const entry = codeInclude[language];
|
|
175808
|
+
if (entry === undefined) {
|
|
175809
|
+
continue;
|
|
175810
|
+
}
|
|
175811
|
+
for (const relPath of collectFiles(projectRoot, entry.patterns)) {
|
|
175812
|
+
if (seen.has(relPath)) {
|
|
175813
|
+
continue;
|
|
175814
|
+
}
|
|
175815
|
+
seen.add(relPath);
|
|
175816
|
+
collected.push({ language, relPath });
|
|
175817
|
+
}
|
|
175818
|
+
}
|
|
175819
|
+
collected.sort((left, right) => comparePaths(left.relPath, right.relPath));
|
|
175820
|
+
return collected;
|
|
175821
|
+
}
|
|
175822
|
+
function codeFileOwners(projectRoot, codeInclude) {
|
|
175823
|
+
const owners = new Map;
|
|
175824
|
+
for (const language of KNOWN_CODE_LANGUAGES) {
|
|
175825
|
+
const entry = codeInclude[language];
|
|
175826
|
+
if (entry === undefined) {
|
|
175827
|
+
continue;
|
|
175828
|
+
}
|
|
175829
|
+
for (const relPath of collectFiles(projectRoot, entry.patterns)) {
|
|
175830
|
+
const existing = owners.get(relPath);
|
|
175831
|
+
if (existing === undefined) {
|
|
175832
|
+
owners.set(relPath, [language]);
|
|
175833
|
+
} else if (!existing.includes(language)) {
|
|
175834
|
+
existing.push(language);
|
|
175835
|
+
}
|
|
175836
|
+
}
|
|
175837
|
+
}
|
|
175838
|
+
return owners;
|
|
175839
|
+
}
|
|
175840
|
+
|
|
175841
|
+
// src/core/config.ts
|
|
175842
|
+
var CONFIG_FILE_NAME = "docbridge.config.json";
|
|
175843
|
+
var EMPTY_CONFIG = {
|
|
175844
|
+
include: { code: {}, docs: [] }
|
|
175845
|
+
};
|
|
175846
|
+
var KNOWN_TOP_LEVEL_KEYS = new Set(["$schema", "include"]);
|
|
175847
|
+
var KNOWN_INCLUDE_KEYS = new Set(["code", "docs"]);
|
|
175848
|
+
var KNOWN_CODE_ENTRY_KEYS = new Set(["patterns", "visibility"]);
|
|
175849
|
+
var LANGUAGE_SUFFIX = {
|
|
175850
|
+
typescript: ".ts",
|
|
175851
|
+
swift: ".swift",
|
|
175852
|
+
dart: ".dart",
|
|
175853
|
+
rust: ".rs"
|
|
175854
|
+
};
|
|
175855
|
+
var LANGUAGE_VISIBILITY = {
|
|
175856
|
+
typescript: ["public", "protected", "private"],
|
|
175857
|
+
swift: ["public", "open", "internal"],
|
|
175858
|
+
dart: ["public"],
|
|
175859
|
+
rust: ["pub", "private"]
|
|
175860
|
+
};
|
|
175861
|
+
function loadConfig(projectRoot) {
|
|
175862
|
+
let rawText;
|
|
175863
|
+
try {
|
|
175864
|
+
rawText = readFileSync2(join2(projectRoot, CONFIG_FILE_NAME), "utf8");
|
|
175865
|
+
} catch {
|
|
175866
|
+
rawText = undefined;
|
|
175867
|
+
}
|
|
175868
|
+
const resolved = resolveConfig(rawText);
|
|
175869
|
+
if (!resolved.ok) {
|
|
175870
|
+
return resolved;
|
|
175871
|
+
}
|
|
175872
|
+
const overlapDiagnostics = detectLanguageOverlap(projectRoot, resolved.config.include.code);
|
|
175873
|
+
if (overlapDiagnostics.length === 0) {
|
|
175874
|
+
return resolved;
|
|
175875
|
+
}
|
|
175876
|
+
return {
|
|
175877
|
+
config: EMPTY_CONFIG,
|
|
175878
|
+
diagnostics: [...resolved.diagnostics, ...overlapDiagnostics],
|
|
175879
|
+
ok: false
|
|
175880
|
+
};
|
|
175881
|
+
}
|
|
175882
|
+
function resolveConfig(rawText) {
|
|
175883
|
+
if (rawText === undefined) {
|
|
175884
|
+
return {
|
|
175885
|
+
config: EMPTY_CONFIG,
|
|
175886
|
+
ok: false,
|
|
175887
|
+
diagnostics: [
|
|
175888
|
+
configDiagnostic("config_file_invalid", CONFIG_FILE_NAME, `${CONFIG_FILE_NAME} was not found. DocBridge requires a configuration file.`)
|
|
175889
|
+
]
|
|
175890
|
+
};
|
|
175891
|
+
}
|
|
175892
|
+
let parsed;
|
|
175893
|
+
try {
|
|
175894
|
+
parsed = JSON.parse(rawText);
|
|
175895
|
+
} catch (error) {
|
|
175896
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
175897
|
+
return {
|
|
175898
|
+
config: EMPTY_CONFIG,
|
|
175899
|
+
ok: false,
|
|
175900
|
+
diagnostics: [
|
|
175901
|
+
configDiagnostic("config_file_invalid", CONFIG_FILE_NAME, `Failed to parse ${CONFIG_FILE_NAME}: ${reason}`)
|
|
175902
|
+
]
|
|
175903
|
+
};
|
|
175904
|
+
}
|
|
175905
|
+
const diagnostics = [];
|
|
175906
|
+
if (!isPlainObject(parsed)) {
|
|
175907
|
+
diagnostics.push(configDiagnostic("config_invalid_value", CONFIG_FILE_NAME, "Configuration must be a JSON object."));
|
|
175908
|
+
return { config: EMPTY_CONFIG, diagnostics, ok: false };
|
|
175909
|
+
}
|
|
175910
|
+
for (const key of Object.keys(parsed)) {
|
|
175911
|
+
if (!KNOWN_TOP_LEVEL_KEYS.has(key)) {
|
|
175912
|
+
diagnostics.push(configDiagnostic("config_unknown_key", key, `Unknown top-level configuration key: ${key}`));
|
|
175913
|
+
}
|
|
175914
|
+
}
|
|
175915
|
+
if ("$schema" in parsed && typeof parsed.$schema !== "string") {
|
|
175916
|
+
diagnostics.push(configDiagnostic("config_invalid_value", "$schema", "`$schema` must be a string."));
|
|
175917
|
+
}
|
|
175918
|
+
const include = parsed.include;
|
|
175919
|
+
if (!isPlainObject(include)) {
|
|
175920
|
+
diagnostics.push(configDiagnostic("config_invalid_value", "include", "`include` must be an object with `code` and `docs`."));
|
|
175921
|
+
return { config: EMPTY_CONFIG, diagnostics, ok: false };
|
|
175922
|
+
}
|
|
175923
|
+
for (const key of Object.keys(include)) {
|
|
175924
|
+
if (!KNOWN_INCLUDE_KEYS.has(key)) {
|
|
175925
|
+
diagnostics.push(configDiagnostic("config_unknown_key", `include.${key}`, `Unknown configuration key under \`include\`: ${key}`));
|
|
175926
|
+
}
|
|
175927
|
+
}
|
|
175928
|
+
const code = validateCodeInclude(include.code, diagnostics);
|
|
175929
|
+
validatePatternArray(include.docs, "docs", ".md", false, diagnostics);
|
|
175930
|
+
const ok = diagnostics.length === 0;
|
|
175931
|
+
const config = ok ? { include: { code, docs: include.docs } } : EMPTY_CONFIG;
|
|
175932
|
+
return { config, diagnostics, ok };
|
|
175933
|
+
}
|
|
175934
|
+
function validateCodeInclude(value, diagnostics) {
|
|
175935
|
+
if (Array.isArray(value)) {
|
|
175936
|
+
diagnostics.push(configDiagnostic("config_invalid_value", "include.code", '`include.code` must be a language-keyed object such as `{ "typescript": { "patterns": ["src/**/*.ts"] } }`, not an array.'));
|
|
175937
|
+
return {};
|
|
175938
|
+
}
|
|
175939
|
+
if (!isPlainObject(value)) {
|
|
175940
|
+
diagnostics.push(configDiagnostic("config_invalid_value", "include.code", "`include.code` must be a language-keyed object."));
|
|
175941
|
+
return {};
|
|
175942
|
+
}
|
|
175943
|
+
const keys = Object.keys(value);
|
|
175944
|
+
if (keys.length === 0) {
|
|
175945
|
+
diagnostics.push(configDiagnostic("config_invalid_value", "include.code", "`include.code` must configure at least one language."));
|
|
175946
|
+
return {};
|
|
175947
|
+
}
|
|
175948
|
+
const result = {};
|
|
175949
|
+
for (const key of keys) {
|
|
175950
|
+
if (!isCodeLanguage(key)) {
|
|
175951
|
+
diagnostics.push(configDiagnostic("config_invalid_value", `include.code.${key}`, `Unknown code language: ${key}. Supported languages: ${KNOWN_CODE_LANGUAGES.join(", ")}.`));
|
|
175952
|
+
continue;
|
|
175953
|
+
}
|
|
175954
|
+
const entry = validateCodeEntry(key, value[key], diagnostics);
|
|
175955
|
+
if (entry !== undefined) {
|
|
175956
|
+
result[key] = entry;
|
|
175957
|
+
}
|
|
175958
|
+
}
|
|
175959
|
+
return result;
|
|
175960
|
+
}
|
|
175961
|
+
function validateCodeEntry(language, value, diagnostics) {
|
|
175962
|
+
const target = `include.code.${language}`;
|
|
175963
|
+
if (!isPlainObject(value)) {
|
|
175964
|
+
diagnostics.push(configDiagnostic("config_invalid_value", target, `\`${target}\` must be an object with a \`patterns\` array. Shorthand arrays are not supported.`));
|
|
175965
|
+
return;
|
|
175966
|
+
}
|
|
175967
|
+
for (const key of Object.keys(value)) {
|
|
175968
|
+
if (!KNOWN_CODE_ENTRY_KEYS.has(key)) {
|
|
175969
|
+
diagnostics.push(configDiagnostic("config_unknown_key", `${target}.${key}`, `Unknown configuration key under \`${target}\`: ${key}`));
|
|
175970
|
+
}
|
|
175971
|
+
}
|
|
175972
|
+
const before = diagnostics.length;
|
|
175973
|
+
validatePatternArray(value.patterns, `code.${language}.patterns`, LANGUAGE_SUFFIX[language], language === "typescript", diagnostics);
|
|
175974
|
+
if ("visibility" in value) {
|
|
175975
|
+
if (!isStringArray(value.visibility)) {
|
|
175976
|
+
diagnostics.push(configDiagnostic("config_invalid_value", `${target}.visibility`, `\`${target}.visibility\` must be an array of strings.`));
|
|
175977
|
+
} else {
|
|
175978
|
+
validateVisibilityOptions(language, value.visibility, target, diagnostics);
|
|
175979
|
+
}
|
|
175980
|
+
}
|
|
175981
|
+
if (diagnostics.length !== before) {
|
|
175982
|
+
return;
|
|
175983
|
+
}
|
|
175984
|
+
const entry = { patterns: value.patterns };
|
|
175985
|
+
if (isStringArray(value.visibility)) {
|
|
175986
|
+
entry.visibility = value.visibility;
|
|
175987
|
+
}
|
|
175988
|
+
return entry;
|
|
175989
|
+
}
|
|
175990
|
+
function validateVisibilityOptions(language, values, target, diagnostics) {
|
|
175991
|
+
const allowed = LANGUAGE_VISIBILITY[language];
|
|
175992
|
+
if (allowed.length === 0) {
|
|
175993
|
+
diagnostics.push(configDiagnostic("config_invalid_value", `${target}.visibility`, `\`${target}.visibility\` is not supported for ${language}.`));
|
|
175994
|
+
return;
|
|
175995
|
+
}
|
|
175996
|
+
for (const value of values) {
|
|
175997
|
+
if (!allowed.includes(value)) {
|
|
175998
|
+
diagnostics.push(configDiagnostic("config_invalid_value", `${target}.visibility`, `Unsupported ${language} visibility: ${value}. Supported values: ${allowed.join(", ")}.`));
|
|
175999
|
+
}
|
|
176000
|
+
}
|
|
176001
|
+
}
|
|
176002
|
+
function validatePatternArray(value, field, requiredSuffix, excludeDeclarationFiles, diagnostics) {
|
|
176003
|
+
const target = `include.${field}`;
|
|
176004
|
+
if (!Array.isArray(value)) {
|
|
176005
|
+
diagnostics.push(configDiagnostic("config_invalid_value", target, `\`${target}\` must be a non-empty array of strings.`));
|
|
176006
|
+
return;
|
|
176007
|
+
}
|
|
176008
|
+
if (value.length === 0) {
|
|
176009
|
+
diagnostics.push(configDiagnostic("config_invalid_value", target, `\`${target}\` must contain at least one pattern.`));
|
|
176010
|
+
return;
|
|
176011
|
+
}
|
|
176012
|
+
for (const pattern of value) {
|
|
176013
|
+
if (typeof pattern !== "string") {
|
|
176014
|
+
diagnostics.push(configDiagnostic("config_invalid_value", target, `\`${target}\` patterns must be strings.`));
|
|
176015
|
+
continue;
|
|
176016
|
+
}
|
|
176017
|
+
const globResult = validateGlobPattern(pattern);
|
|
176018
|
+
if (!globResult.ok) {
|
|
176019
|
+
diagnostics.push(configDiagnostic("config_invalid_value", pattern, `Invalid pattern in \`${target}\`: ${globResult.message}`));
|
|
176020
|
+
continue;
|
|
176021
|
+
}
|
|
176022
|
+
const suffixError = checkSuffix(pattern, requiredSuffix, excludeDeclarationFiles);
|
|
176023
|
+
if (suffixError !== undefined) {
|
|
176024
|
+
diagnostics.push(configDiagnostic("config_invalid_value", pattern, suffixError));
|
|
176025
|
+
}
|
|
176026
|
+
}
|
|
176027
|
+
}
|
|
176028
|
+
function checkSuffix(pattern, requiredSuffix, excludeDeclarationFiles) {
|
|
176029
|
+
if (!pattern.endsWith(requiredSuffix)) {
|
|
176030
|
+
return `Pattern must end with \`${requiredSuffix}\`.`;
|
|
176031
|
+
}
|
|
176032
|
+
if (excludeDeclarationFiles && pattern.endsWith(".d.ts")) {
|
|
176033
|
+
return "Pattern must not target `.d.ts` declaration files.";
|
|
176034
|
+
}
|
|
176035
|
+
return;
|
|
176036
|
+
}
|
|
176037
|
+
function detectLanguageOverlap(projectRoot, code) {
|
|
176038
|
+
const diagnostics = [];
|
|
176039
|
+
for (const [relPath, owners] of codeFileOwners(projectRoot, code)) {
|
|
176040
|
+
if (owners.length > 1) {
|
|
176041
|
+
diagnostics.push(configDiagnostic("config_invalid_value", relPath, `Code file ${relPath} matches multiple configured languages (${owners.join(", ")}). Each code file must belong to exactly one language.`));
|
|
176042
|
+
}
|
|
176043
|
+
}
|
|
176044
|
+
return diagnostics;
|
|
176045
|
+
}
|
|
176046
|
+
function configDiagnostic(code, target, message) {
|
|
176047
|
+
return { severity: "error", code, target, message };
|
|
176048
|
+
}
|
|
176049
|
+
function isPlainObject(value) {
|
|
176050
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
176051
|
+
}
|
|
176052
|
+
function isStringArray(value) {
|
|
176053
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
176054
|
+
}
|
|
176055
|
+
|
|
176056
|
+
// src/core/diagnostics.ts
|
|
176057
|
+
function sortDiagnostics(diagnostics) {
|
|
176058
|
+
return [...diagnostics].toSorted(compareDiagnostics);
|
|
176059
|
+
}
|
|
176060
|
+
function summarizeDiagnostics(diagnostics) {
|
|
176061
|
+
const summary = { errors: 0, warnings: 0 };
|
|
176062
|
+
for (const diagnostic of diagnostics) {
|
|
176063
|
+
if (diagnostic.severity === "error") {
|
|
176064
|
+
summary.errors += 1;
|
|
176065
|
+
} else {
|
|
176066
|
+
summary.warnings += 1;
|
|
176067
|
+
}
|
|
176068
|
+
}
|
|
176069
|
+
return summary;
|
|
176070
|
+
}
|
|
176071
|
+
function formatDiagnostic(diagnostic) {
|
|
176072
|
+
const location = diagnostic.location;
|
|
176073
|
+
if (location === undefined) {
|
|
176074
|
+
return `${diagnostic.target} ${diagnostic.severity} ${diagnostic.code} - ${diagnostic.message}`;
|
|
176075
|
+
}
|
|
176076
|
+
const prefix = `${location.filePath}:${location.line}:${location.column}`;
|
|
176077
|
+
return `${prefix} ${diagnostic.severity} ${diagnostic.code} ${diagnostic.target} - ${diagnostic.message}`;
|
|
176078
|
+
}
|
|
176079
|
+
function formatSummary(summary) {
|
|
176080
|
+
return `Summary: ${summary.errors} ${pluralize("error", summary.errors)}, ${summary.warnings} ${pluralize("warning", summary.warnings)}`;
|
|
176081
|
+
}
|
|
176082
|
+
function collectErroredFiles(diagnostics) {
|
|
176083
|
+
const errored = new Set;
|
|
176084
|
+
for (const diagnostic of diagnostics) {
|
|
176085
|
+
if (diagnostic.code === "file_read_error" || diagnostic.code === "code_parse_error" || isFileScopedScannerDiagnostic(diagnostic)) {
|
|
176086
|
+
errored.add(diagnostic.target);
|
|
176087
|
+
}
|
|
176088
|
+
}
|
|
176089
|
+
return errored;
|
|
176090
|
+
}
|
|
176091
|
+
function isFileScopedScannerDiagnostic(diagnostic) {
|
|
176092
|
+
return (diagnostic.code === "code_scanner_unavailable" || diagnostic.code === "code_scanner_failed") && diagnostic.language !== undefined && diagnostic.target !== diagnostic.language;
|
|
176093
|
+
}
|
|
176094
|
+
function compareDiagnostics(left, right) {
|
|
176095
|
+
const leftLocation = left.location;
|
|
176096
|
+
const rightLocation = right.location;
|
|
176097
|
+
if (leftLocation === undefined && rightLocation !== undefined) {
|
|
176098
|
+
return -1;
|
|
176099
|
+
}
|
|
176100
|
+
if (leftLocation !== undefined && rightLocation === undefined) {
|
|
176101
|
+
return 1;
|
|
176102
|
+
}
|
|
176103
|
+
if (leftLocation !== undefined && rightLocation !== undefined) {
|
|
176104
|
+
return compareString(leftLocation.filePath, rightLocation.filePath) || compareNumber(leftLocation.line, rightLocation.line) || compareNumber(leftLocation.column, rightLocation.column) || compareString(left.code, right.code) || compareString(left.target, right.target);
|
|
176105
|
+
}
|
|
176106
|
+
return compareString(left.code, right.code) || compareString(left.target, right.target);
|
|
176107
|
+
}
|
|
176108
|
+
function compareString(left, right) {
|
|
176109
|
+
return left.localeCompare(right);
|
|
176110
|
+
}
|
|
176111
|
+
function compareNumber(left, right) {
|
|
176112
|
+
return left - right;
|
|
176113
|
+
}
|
|
176114
|
+
function pluralize(word, count) {
|
|
176115
|
+
return count === 1 ? word : `${word}s`;
|
|
176116
|
+
}
|
|
176117
|
+
|
|
176118
|
+
// src/core/endpoint.ts
|
|
176119
|
+
function filePathOf(endpoint) {
|
|
176120
|
+
const hashIndex = endpoint.indexOf("#");
|
|
176121
|
+
return hashIndex === -1 ? endpoint : endpoint.slice(0, hashIndex);
|
|
176122
|
+
}
|
|
176123
|
+
function fragmentOf(endpoint) {
|
|
176124
|
+
const hashIndex = endpoint.indexOf("#");
|
|
176125
|
+
return hashIndex === -1 ? endpoint : endpoint.slice(hashIndex + 1);
|
|
176126
|
+
}
|
|
176127
|
+
function compareEndpointOrder(left, right) {
|
|
176128
|
+
return left.filePath.localeCompare(right.filePath) || left.line - right.line || left.column - right.column || left.endpoint.localeCompare(right.endpoint);
|
|
176129
|
+
}
|
|
176130
|
+
function endpointRange(element) {
|
|
176131
|
+
const range = element.kind === "code" ? element.nameRange : element.headingTextRange;
|
|
176132
|
+
if (range !== undefined) {
|
|
176133
|
+
return range;
|
|
176134
|
+
}
|
|
176135
|
+
const { line, column } = element.location;
|
|
176136
|
+
return { start: { line, column }, end: { line, column } };
|
|
176137
|
+
}
|
|
176138
|
+
|
|
176139
|
+
// src/core/graph.ts
|
|
176140
|
+
function buildLinkGraph(codeFiles, docFiles) {
|
|
176141
|
+
const codeByEndpoint = new Map;
|
|
176142
|
+
for (const file of codeFiles) {
|
|
176143
|
+
for (const symbol of file.symbols) {
|
|
176144
|
+
if (!codeByEndpoint.has(symbol.endpoint)) {
|
|
176145
|
+
codeByEndpoint.set(symbol.endpoint, symbol);
|
|
176146
|
+
}
|
|
176147
|
+
}
|
|
176148
|
+
}
|
|
176149
|
+
const docByEndpoint = new Map;
|
|
176150
|
+
for (const file of docFiles) {
|
|
176151
|
+
for (const anchor of file.anchors) {
|
|
176152
|
+
if (!docByEndpoint.has(anchor.endpoint)) {
|
|
176153
|
+
docByEndpoint.set(anchor.endpoint, anchor);
|
|
176154
|
+
}
|
|
176155
|
+
}
|
|
176156
|
+
}
|
|
176157
|
+
const counterparts = new Map;
|
|
176158
|
+
const addEdge = (a, b) => {
|
|
176159
|
+
addTo(counterparts, a, b);
|
|
176160
|
+
addTo(counterparts, b, a);
|
|
176161
|
+
};
|
|
176162
|
+
for (const file of codeFiles) {
|
|
176163
|
+
for (const link of file.links) {
|
|
176164
|
+
if (codeByEndpoint.has(link.source) && docByEndpoint.has(link.target)) {
|
|
176165
|
+
addEdge(link.source, link.target);
|
|
176166
|
+
}
|
|
176167
|
+
}
|
|
176168
|
+
}
|
|
176169
|
+
for (const file of docFiles) {
|
|
176170
|
+
for (const link of file.links) {
|
|
176171
|
+
if (docByEndpoint.has(link.source) && codeByEndpoint.has(link.target)) {
|
|
176172
|
+
addEdge(link.source, link.target);
|
|
176173
|
+
}
|
|
176174
|
+
}
|
|
176175
|
+
}
|
|
176176
|
+
return { codeByEndpoint, docByEndpoint, counterparts };
|
|
176177
|
+
}
|
|
176178
|
+
function counterpartsOf(graph, endpoint) {
|
|
176179
|
+
const targets = graph.counterparts.get(endpoint);
|
|
176180
|
+
if (targets === undefined) {
|
|
176181
|
+
return [];
|
|
176182
|
+
}
|
|
176183
|
+
const resolved = [];
|
|
176184
|
+
for (const target of targets) {
|
|
176185
|
+
const code = graph.codeByEndpoint.get(target);
|
|
176186
|
+
if (code !== undefined) {
|
|
176187
|
+
resolved.push(code);
|
|
176188
|
+
continue;
|
|
176189
|
+
}
|
|
176190
|
+
const doc = graph.docByEndpoint.get(target);
|
|
176191
|
+
if (doc !== undefined) {
|
|
176192
|
+
resolved.push(doc);
|
|
176193
|
+
}
|
|
176194
|
+
}
|
|
176195
|
+
return resolved.toSorted(compareEndpoints);
|
|
176196
|
+
}
|
|
176197
|
+
function addTo(map, key, value) {
|
|
176198
|
+
const existing = map.get(key);
|
|
176199
|
+
if (existing === undefined) {
|
|
176200
|
+
map.set(key, new Set([value]));
|
|
176201
|
+
return;
|
|
176202
|
+
}
|
|
176203
|
+
existing.add(value);
|
|
176204
|
+
}
|
|
176205
|
+
function compareEndpoints(left, right) {
|
|
176206
|
+
return compareEndpointOrder({ ...left.location, endpoint: left.endpoint }, { ...right.location, endpoint: right.endpoint });
|
|
176207
|
+
}
|
|
176208
|
+
|
|
176209
|
+
// src/core/code-adapter-registry.ts
|
|
176210
|
+
var codeAdapters = {};
|
|
176211
|
+
|
|
176212
|
+
// src/core/scanner-executable.ts
|
|
176213
|
+
import { accessSync, chmodSync, constants, existsSync, realpathSync, statSync } from "node:fs";
|
|
176214
|
+
import { dirname, join as join3, resolve } from "node:path";
|
|
176215
|
+
import { fileURLToPath } from "node:url";
|
|
176216
|
+
|
|
176217
|
+
// src/core/error.ts
|
|
176218
|
+
function reasonOf(error) {
|
|
176219
|
+
return error instanceof Error ? error.message : String(error);
|
|
176220
|
+
}
|
|
176221
|
+
|
|
176222
|
+
// src/core/scanner-executable.ts
|
|
176223
|
+
var SUPPORTED_SCANNER_PLATFORM_KEYS = ["darwin-arm64", "linux-x64"];
|
|
176224
|
+
var SCANNER_EXECUTABLE_NAMES = {
|
|
176225
|
+
swift: "docbridge-swift-scanner",
|
|
176226
|
+
dart: "docbridge_dart_scanner",
|
|
176227
|
+
rust: "docbridge-rust-scanner"
|
|
176228
|
+
};
|
|
176229
|
+
function scannerPlatformKey() {
|
|
176230
|
+
return `${process.platform}-${process.arch}`;
|
|
176231
|
+
}
|
|
176232
|
+
function scannerExecutableName(language) {
|
|
176233
|
+
return SCANNER_EXECUTABLE_NAMES[language];
|
|
176234
|
+
}
|
|
176235
|
+
function resolveScannerWorkerCommand(language, options = {}) {
|
|
176236
|
+
const platformKey = options.platformKey ?? scannerPlatformKey();
|
|
176237
|
+
const platformSupported = isSupportedScannerPlatformKey(platformKey);
|
|
176238
|
+
const candidates = scannerExecutableCandidates(language, platformKey, platformSupported, options);
|
|
176239
|
+
const found = candidates.find((candidate) => existsSync(candidate));
|
|
176240
|
+
if (found !== undefined) {
|
|
176241
|
+
const repair = ensureExecutable(found, options.chmod ?? chmodSync);
|
|
176242
|
+
if (!repair.ok) {
|
|
176243
|
+
return { ok: false, diagnostic: scannerUnavailableDiagnostic(language, repair.reason) };
|
|
176244
|
+
}
|
|
176245
|
+
return { ok: true, command: [found] };
|
|
176246
|
+
}
|
|
176247
|
+
if (!platformSupported) {
|
|
176248
|
+
return {
|
|
176249
|
+
ok: false,
|
|
176250
|
+
diagnostic: scannerUnavailableDiagnostic(language, `platform ${platformKey} is unsupported; supported platforms: ${SUPPORTED_SCANNER_PLATFORM_KEYS.join(", ")}`)
|
|
176251
|
+
};
|
|
176252
|
+
}
|
|
176253
|
+
return {
|
|
176254
|
+
ok: false,
|
|
176255
|
+
diagnostic: scannerUnavailableDiagnostic(language, `missing ${scannerExecutableName(language)} for platform ${platformKey}; supported platforms: ${SUPPORTED_SCANNER_PLATFORM_KEYS.join(", ")}`)
|
|
176256
|
+
};
|
|
176257
|
+
}
|
|
176258
|
+
function scannerExecutableCandidates(language, platformKey, platformSupported, options) {
|
|
176259
|
+
const sourceRoot = options.sourceRoot ?? sourceRootPath();
|
|
176260
|
+
const distRoot = options.distRoot ?? distRootPath();
|
|
176261
|
+
const executable = scannerExecutableName(language);
|
|
176262
|
+
if (language === "swift") {
|
|
176263
|
+
return [
|
|
176264
|
+
join3(sourceRoot, "packages/swift-scanner/.build/release", executable),
|
|
176265
|
+
join3(sourceRoot, "packages/swift-scanner/.build/debug", executable),
|
|
176266
|
+
...platformSupported ? [join3(distRoot, "bin", platformKey, executable)] : []
|
|
176267
|
+
];
|
|
176268
|
+
}
|
|
176269
|
+
if (language === "rust") {
|
|
176270
|
+
return [
|
|
176271
|
+
join3(sourceRoot, "packages/rust-scanner/target/release", executable),
|
|
176272
|
+
join3(sourceRoot, "packages/rust-scanner/target/debug", executable),
|
|
176273
|
+
...platformSupported ? [join3(distRoot, "bin", platformKey, executable)] : []
|
|
176274
|
+
];
|
|
176275
|
+
}
|
|
176276
|
+
return [
|
|
176277
|
+
join3(sourceRoot, "packages/dart-scanner/bin", executable),
|
|
176278
|
+
...platformSupported ? [join3(distRoot, "bin", platformKey, executable)] : []
|
|
176279
|
+
];
|
|
176280
|
+
}
|
|
176281
|
+
function ensureExecutable(path, chmod) {
|
|
176282
|
+
let mode;
|
|
176283
|
+
try {
|
|
176284
|
+
mode = statSync(path).mode;
|
|
176285
|
+
} catch (error) {
|
|
176286
|
+
return { ok: false, reason: `cannot stat ${path}: ${reasonOf(error)}` };
|
|
176287
|
+
}
|
|
176288
|
+
if (isExecutableByThisProcess(path)) {
|
|
176289
|
+
return { ok: true };
|
|
176290
|
+
}
|
|
176291
|
+
try {
|
|
176292
|
+
chmod(path, mode | 73);
|
|
176293
|
+
} catch (error) {
|
|
176294
|
+
return {
|
|
176295
|
+
ok: false,
|
|
176296
|
+
reason: `${path} is not executable (mode ${formatMode(mode)}) and the executable ` + `bit could not be restored: ${reasonOf(error)}; run \`chmod +x ${path}\` ` + `or reinstall DocBridge into a writable location`
|
|
176297
|
+
};
|
|
176298
|
+
}
|
|
176299
|
+
return { ok: true };
|
|
176300
|
+
}
|
|
176301
|
+
function isExecutableByThisProcess(path) {
|
|
176302
|
+
try {
|
|
176303
|
+
accessSync(path, constants.X_OK);
|
|
176304
|
+
return true;
|
|
176305
|
+
} catch {
|
|
176306
|
+
return false;
|
|
176307
|
+
}
|
|
176308
|
+
}
|
|
176309
|
+
function formatMode(mode) {
|
|
176310
|
+
return `0${(mode & 4095).toString(8).padStart(3, "0")}`;
|
|
176311
|
+
}
|
|
176312
|
+
function isSupportedScannerPlatformKey(platformKey) {
|
|
176313
|
+
return SUPPORTED_SCANNER_PLATFORM_KEYS.includes(platformKey);
|
|
176314
|
+
}
|
|
176315
|
+
function scannerRootsFromModuleUrl(moduleUrl) {
|
|
176316
|
+
const modulePath = fileURLToPath(moduleUrl);
|
|
176317
|
+
let resolved;
|
|
176318
|
+
try {
|
|
176319
|
+
resolved = realpathSync(modulePath);
|
|
176320
|
+
} catch {
|
|
176321
|
+
resolved = modulePath;
|
|
176322
|
+
}
|
|
176323
|
+
const moduleDir = dirname(resolved);
|
|
176324
|
+
return { distRoot: moduleDir, sourceRoot: resolve(moduleDir, "..", "..") };
|
|
176325
|
+
}
|
|
176326
|
+
function sourceRootPath() {
|
|
176327
|
+
return scannerRootsFromModuleUrl(import.meta.url).sourceRoot;
|
|
176328
|
+
}
|
|
176329
|
+
function distRootPath() {
|
|
176330
|
+
return scannerRootsFromModuleUrl(import.meta.url).distRoot;
|
|
176331
|
+
}
|
|
176332
|
+
function scannerUnavailableDiagnostic(language, reason) {
|
|
176333
|
+
const label = language.charAt(0).toUpperCase() + language.slice(1);
|
|
176334
|
+
return {
|
|
176335
|
+
severity: "error",
|
|
176336
|
+
code: "code_scanner_unavailable",
|
|
176337
|
+
language,
|
|
176338
|
+
target: language,
|
|
176339
|
+
message: `${label} scanner worker is unavailable: ${reason}`
|
|
176340
|
+
};
|
|
176341
|
+
}
|
|
176342
|
+
|
|
175805
176343
|
// src/core/scanner-worker.ts
|
|
175806
176344
|
var import__2020 = __toESM(require_2020(), 1);
|
|
175807
176345
|
import { spawnSync } from "node:child_process";
|
|
175808
176346
|
import { mkdirSync } from "node:fs";
|
|
175809
176347
|
import { tmpdir } from "node:os";
|
|
175810
|
-
import { dirname, join as
|
|
176348
|
+
import { dirname as dirname2, join as join4 } from "node:path";
|
|
175811
176349
|
// schemas/common-output.schema.json
|
|
175812
176350
|
var common_output_schema_default = {
|
|
175813
176351
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
@@ -175897,6 +176435,7 @@ var scanner_worker_schema_default = {
|
|
|
175897
176435
|
canonicalId: { type: "string" },
|
|
175898
176436
|
endpoint: { type: "string" },
|
|
175899
176437
|
location: { $ref: "common-output.schema.json#/$defs/location" },
|
|
176438
|
+
isMember: { type: "boolean" },
|
|
175900
176439
|
nameRange: { $ref: "common-output.schema.json#/$defs/range" },
|
|
175901
176440
|
declarationRange: { $ref: "common-output.schema.json#/$defs/range" },
|
|
175902
176441
|
signatureRange: { $ref: "common-output.schema.json#/$defs/range" }
|
|
@@ -176015,7 +176554,7 @@ function invokeScannerWorker(request, command, run = runScannerWorkerProcess) {
|
|
|
176015
176554
|
if (!processResult.ok) {
|
|
176016
176555
|
return {
|
|
176017
176556
|
ok: false,
|
|
176018
|
-
diagnostic:
|
|
176557
|
+
diagnostic: scannerUnavailableDiagnostic2(request.language, processResult.error, command[0]),
|
|
176019
176558
|
stderr: processResult.stderr
|
|
176020
176559
|
};
|
|
176021
176560
|
}
|
|
@@ -176056,7 +176595,7 @@ function invokeScannerWorker(request, command, run = runScannerWorkerProcess) {
|
|
|
176056
176595
|
}
|
|
176057
176596
|
function clangModuleCachePath() {
|
|
176058
176597
|
const owner = typeof process.getuid === "function" ? process.getuid() : "shared";
|
|
176059
|
-
return
|
|
176598
|
+
return join4(tmpdir(), `docbridge-clang-module-cache-${owner}`);
|
|
176060
176599
|
}
|
|
176061
176600
|
function runScannerWorkerProcess(input) {
|
|
176062
176601
|
try {
|
|
@@ -176132,7 +176671,7 @@ function responseFilesMatchRequest(responseFiles, requestFiles) {
|
|
|
176132
176671
|
function isRecord(value) {
|
|
176133
176672
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
176134
176673
|
}
|
|
176135
|
-
function
|
|
176674
|
+
function scannerUnavailableDiagnostic2(language, error, executable) {
|
|
176136
176675
|
const label = languageLabel(language);
|
|
176137
176676
|
return {
|
|
176138
176677
|
severity: "error",
|
|
@@ -176147,7 +176686,7 @@ function spawnFailureReason(error, executable) {
|
|
|
176147
176686
|
if (!isExecDenied(error) || executable === undefined) {
|
|
176148
176687
|
return reason;
|
|
176149
176688
|
}
|
|
176150
|
-
return `${reason}; the scanner is executable but ${
|
|
176689
|
+
return `${reason}; the scanner is executable but ${dirname2(executable)} refuses to ` + `execute it, which a \`noexec\` mount does. Install DocBridge as a project ` + `dependency, or point the installer cache at an exec-capable directory, ` + `instead of running through \`bunx\``;
|
|
176151
176690
|
}
|
|
176152
176691
|
function isExecDenied(error) {
|
|
176153
176692
|
if (!(error instanceof Error)) {
|
|
@@ -176283,7 +176822,7 @@ function scanTypeScript(filePath, content, options = {}) {
|
|
|
176283
176822
|
for (const declaration of declarations) {
|
|
176284
176823
|
const endpoint = `${filePath}#${declaration.canonicalId}`;
|
|
176285
176824
|
if (!documentedEndpoints.has(endpoint)) {
|
|
176286
|
-
if (!
|
|
176825
|
+
if (!undocumentedSeen.has(endpoint)) {
|
|
176287
176826
|
undocumentedSeen.add(endpoint);
|
|
176288
176827
|
undocumentedSymbols.push(makeCodeSymbol(filePath, endpoint, declaration));
|
|
176289
176828
|
}
|
|
@@ -176609,6 +177148,9 @@ function makeCodeSymbol(filePath, endpoint, declaration) {
|
|
|
176609
177148
|
endpoint,
|
|
176610
177149
|
location: declaration.location
|
|
176611
177150
|
};
|
|
177151
|
+
if (declaration.isMember) {
|
|
177152
|
+
symbol.isMember = true;
|
|
177153
|
+
}
|
|
176612
177154
|
if (declaration.nameRange !== undefined) {
|
|
176613
177155
|
symbol.nameRange = declaration.nameRange;
|
|
176614
177156
|
}
|
|
@@ -176693,54 +177235,13 @@ function duplicateLinkDiagnostic(source, target, location, range) {
|
|
|
176693
177235
|
return diagnostic;
|
|
176694
177236
|
}
|
|
176695
177237
|
|
|
176696
|
-
// src/core/code-
|
|
176697
|
-
var KNOWN_CODE_LANGUAGES = [
|
|
176698
|
-
"typescript",
|
|
176699
|
-
"swift",
|
|
176700
|
-
"dart",
|
|
176701
|
-
"rust"
|
|
176702
|
-
];
|
|
176703
|
-
function isCodeLanguage(value) {
|
|
176704
|
-
return KNOWN_CODE_LANGUAGES.includes(value);
|
|
176705
|
-
}
|
|
177238
|
+
// src/core/code-scan.ts
|
|
176706
177239
|
Object.assign(codeAdapters, {
|
|
176707
177240
|
typescript: typeScriptAdapter,
|
|
176708
177241
|
swift: createScannerWorkerAdapter("swift", (_projectRoot) => resolveScannerWorkerCommand("swift")),
|
|
176709
177242
|
dart: createScannerWorkerAdapter("dart", (_projectRoot) => resolveScannerWorkerCommand("dart")),
|
|
176710
177243
|
rust: createScannerWorkerAdapter("rust", (_projectRoot) => resolveScannerWorkerCommand("rust"))
|
|
176711
177244
|
});
|
|
176712
|
-
var SUPPORTED_SCANNER_PLATFORM_KEYS = ["darwin-arm64", "linux-x64"];
|
|
176713
|
-
var SCANNER_EXECUTABLE_NAMES = {
|
|
176714
|
-
swift: "docbridge-swift-scanner",
|
|
176715
|
-
dart: "docbridge_dart_scanner",
|
|
176716
|
-
rust: "docbridge-rust-scanner"
|
|
176717
|
-
};
|
|
176718
|
-
function scannerPlatformKey() {
|
|
176719
|
-
return `${process.platform}-${process.arch}`;
|
|
176720
|
-
}
|
|
176721
|
-
function resolveScannerWorkerCommand(language, options = {}) {
|
|
176722
|
-
const platformKey = options.platformKey ?? scannerPlatformKey();
|
|
176723
|
-
const platformSupported = isSupportedScannerPlatformKey(platformKey);
|
|
176724
|
-
const candidates = scannerExecutableCandidates(language, platformKey, platformSupported, options);
|
|
176725
|
-
const found = candidates.find((candidate) => existsSync(candidate));
|
|
176726
|
-
if (found !== undefined) {
|
|
176727
|
-
const repair = ensureExecutable(found, options.chmod ?? chmodSync);
|
|
176728
|
-
if (!repair.ok) {
|
|
176729
|
-
return { ok: false, diagnostic: scannerUnavailableDiagnostic2(language, repair.reason) };
|
|
176730
|
-
}
|
|
176731
|
-
return { ok: true, command: [found] };
|
|
176732
|
-
}
|
|
176733
|
-
if (!platformSupported) {
|
|
176734
|
-
return {
|
|
176735
|
-
ok: false,
|
|
176736
|
-
diagnostic: scannerUnavailableDiagnostic2(language, `platform ${platformKey} is unsupported; supported platforms: ${SUPPORTED_SCANNER_PLATFORM_KEYS.join(", ")}`)
|
|
176737
|
-
};
|
|
176738
|
-
}
|
|
176739
|
-
return {
|
|
176740
|
-
ok: false,
|
|
176741
|
-
diagnostic: scannerUnavailableDiagnostic2(language, `missing ${scannerExecutableName(language)} for platform ${platformKey}; supported platforms: ${SUPPORTED_SCANNER_PLATFORM_KEYS.join(", ")}`)
|
|
176742
|
-
};
|
|
176743
|
-
}
|
|
176744
177245
|
function getCodeAdapter(language) {
|
|
176745
177246
|
return codeAdapters[language];
|
|
176746
177247
|
}
|
|
@@ -176774,25 +177275,6 @@ function createScannerWorkerAdapter(language, command, adapterOptions = {}) {
|
|
|
176774
177275
|
}
|
|
176775
177276
|
};
|
|
176776
177277
|
}
|
|
176777
|
-
function collectCodeFiles(projectRoot, codeInclude) {
|
|
176778
|
-
const collected = [];
|
|
176779
|
-
const seen = new Set;
|
|
176780
|
-
for (const language of KNOWN_CODE_LANGUAGES) {
|
|
176781
|
-
const entry = codeInclude[language];
|
|
176782
|
-
if (entry === undefined) {
|
|
176783
|
-
continue;
|
|
176784
|
-
}
|
|
176785
|
-
for (const relPath of collectFiles(projectRoot, entry.patterns)) {
|
|
176786
|
-
if (seen.has(relPath)) {
|
|
176787
|
-
continue;
|
|
176788
|
-
}
|
|
176789
|
-
seen.add(relPath);
|
|
176790
|
-
collected.push({ language, relPath });
|
|
176791
|
-
}
|
|
176792
|
-
}
|
|
176793
|
-
collected.sort((left, right) => comparePaths(left.relPath, right.relPath));
|
|
176794
|
-
return collected;
|
|
176795
|
-
}
|
|
176796
177278
|
function scanCodeFiles(projectRoot, files, codeInclude, read, onContent) {
|
|
176797
177279
|
const codeFiles = [];
|
|
176798
177280
|
const diagnostics = [];
|
|
@@ -176817,24 +177299,6 @@ function scanCodeFiles(projectRoot, files, codeInclude, read, onContent) {
|
|
|
176817
177299
|
}
|
|
176818
177300
|
return { codeFiles, diagnostics };
|
|
176819
177301
|
}
|
|
176820
|
-
function codeFileOwners(projectRoot, codeInclude) {
|
|
176821
|
-
const owners = new Map;
|
|
176822
|
-
for (const language of KNOWN_CODE_LANGUAGES) {
|
|
176823
|
-
const entry = codeInclude[language];
|
|
176824
|
-
if (entry === undefined) {
|
|
176825
|
-
continue;
|
|
176826
|
-
}
|
|
176827
|
-
for (const relPath of collectFiles(projectRoot, entry.patterns)) {
|
|
176828
|
-
const existing = owners.get(relPath);
|
|
176829
|
-
if (existing === undefined) {
|
|
176830
|
-
owners.set(relPath, [language]);
|
|
176831
|
-
} else if (!existing.includes(language)) {
|
|
176832
|
-
existing.push(language);
|
|
176833
|
-
}
|
|
176834
|
-
}
|
|
176835
|
-
}
|
|
176836
|
-
return owners;
|
|
176837
|
-
}
|
|
176838
177302
|
function emptyScan(language, filePath) {
|
|
176839
177303
|
return {
|
|
176840
177304
|
language,
|
|
@@ -176851,448 +177315,717 @@ function fileScopedScannerDiagnostic(diagnostic, filePath) {
|
|
|
176851
177315
|
function normalizeScannerCommand(value) {
|
|
176852
177316
|
return Array.isArray(value) ? { ok: true, command: value } : value;
|
|
176853
177317
|
}
|
|
176854
|
-
|
|
176855
|
-
|
|
176856
|
-
|
|
176857
|
-
|
|
176858
|
-
|
|
176859
|
-
|
|
176860
|
-
|
|
176861
|
-
|
|
176862
|
-
|
|
176863
|
-
|
|
176864
|
-
|
|
176865
|
-
if (language === "rust") {
|
|
176866
|
-
return [
|
|
176867
|
-
join3(sourceRoot, "packages/rust-scanner/target/release", executable),
|
|
176868
|
-
join3(sourceRoot, "packages/rust-scanner/target/debug", executable),
|
|
176869
|
-
...platformSupported ? [join3(distRoot, "bin", platformKey, executable)] : []
|
|
176870
|
-
];
|
|
177318
|
+
|
|
177319
|
+
// src/core/link-manifest.ts
|
|
177320
|
+
import { readFileSync as readFileSync3 } from "node:fs";
|
|
177321
|
+
import { join as join5 } from "node:path";
|
|
177322
|
+
|
|
177323
|
+
// src/core/json-source.ts
|
|
177324
|
+
class JsonSyntaxError extends Error {
|
|
177325
|
+
position;
|
|
177326
|
+
constructor(message, position) {
|
|
177327
|
+
super(message);
|
|
177328
|
+
this.position = position;
|
|
176871
177329
|
}
|
|
176872
|
-
return [
|
|
176873
|
-
join3(sourceRoot, "packages/dart-scanner/bin", executable),
|
|
176874
|
-
...platformSupported ? [join3(distRoot, "bin", platformKey, executable)] : []
|
|
176875
|
-
];
|
|
176876
177330
|
}
|
|
176877
|
-
|
|
176878
|
-
|
|
177331
|
+
var ESCAPE_VALUES = {
|
|
177332
|
+
'"': '"',
|
|
177333
|
+
"\\": "\\",
|
|
177334
|
+
"/": "/",
|
|
177335
|
+
b: "\b",
|
|
177336
|
+
f: "\f",
|
|
177337
|
+
n: `
|
|
177338
|
+
`,
|
|
177339
|
+
r: "\r",
|
|
177340
|
+
t: "\t"
|
|
177341
|
+
};
|
|
177342
|
+
function parseJsonSource(text) {
|
|
177343
|
+
const reader = new JsonReader(text);
|
|
176879
177344
|
try {
|
|
176880
|
-
|
|
177345
|
+
reader.skipWhitespace();
|
|
177346
|
+
const root = reader.readValue();
|
|
177347
|
+
reader.skipWhitespace();
|
|
177348
|
+
if (!reader.atEnd()) {
|
|
177349
|
+
reader.fail("Unexpected trailing content after the JSON value.");
|
|
177350
|
+
}
|
|
177351
|
+
return { ok: true, root };
|
|
176881
177352
|
} catch (error) {
|
|
176882
|
-
|
|
177353
|
+
if (error instanceof JsonSyntaxError) {
|
|
177354
|
+
return { ok: false, message: error.message, position: error.position };
|
|
177355
|
+
}
|
|
177356
|
+
throw error;
|
|
176883
177357
|
}
|
|
176884
|
-
|
|
176885
|
-
|
|
177358
|
+
}
|
|
177359
|
+
|
|
177360
|
+
class JsonReader {
|
|
177361
|
+
text;
|
|
177362
|
+
index = 0;
|
|
177363
|
+
line = 1;
|
|
177364
|
+
column = 1;
|
|
177365
|
+
constructor(text) {
|
|
177366
|
+
this.text = text;
|
|
177367
|
+
}
|
|
177368
|
+
atEnd() {
|
|
177369
|
+
return this.index >= this.text.length;
|
|
177370
|
+
}
|
|
177371
|
+
fail(message) {
|
|
177372
|
+
throw new JsonSyntaxError(message, this.position());
|
|
177373
|
+
}
|
|
177374
|
+
position() {
|
|
177375
|
+
return { line: this.line, column: this.column };
|
|
177376
|
+
}
|
|
177377
|
+
skipWhitespace() {
|
|
177378
|
+
while (!this.atEnd()) {
|
|
177379
|
+
const char = this.text[this.index] ?? "";
|
|
177380
|
+
if (char !== " " && char !== "\t" && char !== `
|
|
177381
|
+
` && char !== "\r") {
|
|
177382
|
+
return;
|
|
177383
|
+
}
|
|
177384
|
+
this.advance();
|
|
177385
|
+
}
|
|
177386
|
+
}
|
|
177387
|
+
readValue() {
|
|
177388
|
+
if (this.atEnd()) {
|
|
177389
|
+
this.fail("Unexpected end of input where a JSON value was expected.");
|
|
177390
|
+
}
|
|
177391
|
+
const char = this.text[this.index] ?? "";
|
|
177392
|
+
switch (char) {
|
|
177393
|
+
case "{":
|
|
177394
|
+
return this.readObject();
|
|
177395
|
+
case "[":
|
|
177396
|
+
return this.readArray();
|
|
177397
|
+
case '"':
|
|
177398
|
+
return this.readString();
|
|
177399
|
+
case "t":
|
|
177400
|
+
return this.readKeyword("true", true);
|
|
177401
|
+
case "f":
|
|
177402
|
+
return this.readKeyword("false", false);
|
|
177403
|
+
case "n":
|
|
177404
|
+
return this.readNull();
|
|
177405
|
+
default:
|
|
177406
|
+
return this.readNumber();
|
|
177407
|
+
}
|
|
176886
177408
|
}
|
|
176887
|
-
|
|
176888
|
-
|
|
176889
|
-
|
|
176890
|
-
|
|
176891
|
-
|
|
176892
|
-
|
|
176893
|
-
|
|
177409
|
+
readObject() {
|
|
177410
|
+
const start = this.position();
|
|
177411
|
+
this.expect("{");
|
|
177412
|
+
const members = [];
|
|
177413
|
+
this.skipWhitespace();
|
|
177414
|
+
if (this.peek() === "}") {
|
|
177415
|
+
this.advance();
|
|
177416
|
+
return { kind: "object", members, range: { start, end: this.position() } };
|
|
177417
|
+
}
|
|
177418
|
+
for (;; ) {
|
|
177419
|
+
this.skipWhitespace();
|
|
177420
|
+
if (this.peek() !== '"') {
|
|
177421
|
+
this.fail("Expected a quoted object key.");
|
|
177422
|
+
}
|
|
177423
|
+
const keyNode = this.readString();
|
|
177424
|
+
this.skipWhitespace();
|
|
177425
|
+
this.expect(":");
|
|
177426
|
+
this.skipWhitespace();
|
|
177427
|
+
const value = this.readValue();
|
|
177428
|
+
members.push({ key: keyNode.value, keyRange: keyNode.range, value });
|
|
177429
|
+
this.skipWhitespace();
|
|
177430
|
+
const next = this.peek();
|
|
177431
|
+
if (next === ",") {
|
|
177432
|
+
this.advance();
|
|
177433
|
+
continue;
|
|
177434
|
+
}
|
|
177435
|
+
if (next === "}") {
|
|
177436
|
+
this.advance();
|
|
177437
|
+
return { kind: "object", members, range: { start, end: this.position() } };
|
|
177438
|
+
}
|
|
177439
|
+
this.fail("Expected `,` or `}` in the object.");
|
|
177440
|
+
}
|
|
176894
177441
|
}
|
|
176895
|
-
|
|
176896
|
-
|
|
176897
|
-
|
|
176898
|
-
|
|
176899
|
-
|
|
176900
|
-
|
|
176901
|
-
|
|
176902
|
-
|
|
177442
|
+
readArray() {
|
|
177443
|
+
const start = this.position();
|
|
177444
|
+
this.expect("[");
|
|
177445
|
+
const elements = [];
|
|
177446
|
+
this.skipWhitespace();
|
|
177447
|
+
if (this.peek() === "]") {
|
|
177448
|
+
this.advance();
|
|
177449
|
+
return { kind: "array", elements, range: { start, end: this.position() } };
|
|
177450
|
+
}
|
|
177451
|
+
for (;; ) {
|
|
177452
|
+
this.skipWhitespace();
|
|
177453
|
+
elements.push(this.readValue());
|
|
177454
|
+
this.skipWhitespace();
|
|
177455
|
+
const next = this.peek();
|
|
177456
|
+
if (next === ",") {
|
|
177457
|
+
this.advance();
|
|
177458
|
+
continue;
|
|
177459
|
+
}
|
|
177460
|
+
if (next === "]") {
|
|
177461
|
+
this.advance();
|
|
177462
|
+
return { kind: "array", elements, range: { start, end: this.position() } };
|
|
177463
|
+
}
|
|
177464
|
+
this.fail("Expected `,` or `]` in the array.");
|
|
177465
|
+
}
|
|
176903
177466
|
}
|
|
176904
|
-
|
|
176905
|
-
|
|
176906
|
-
|
|
176907
|
-
|
|
176908
|
-
|
|
176909
|
-
|
|
176910
|
-
|
|
176911
|
-
|
|
176912
|
-
|
|
176913
|
-
|
|
176914
|
-
|
|
176915
|
-
|
|
176916
|
-
|
|
176917
|
-
|
|
177467
|
+
readString() {
|
|
177468
|
+
const start = this.position();
|
|
177469
|
+
this.expect('"');
|
|
177470
|
+
const contentStart = this.position();
|
|
177471
|
+
let value = "";
|
|
177472
|
+
for (;; ) {
|
|
177473
|
+
if (this.atEnd()) {
|
|
177474
|
+
this.fail("Unterminated string.");
|
|
177475
|
+
}
|
|
177476
|
+
const char = this.text[this.index] ?? "";
|
|
177477
|
+
if (char === '"') {
|
|
177478
|
+
const contentEnd = this.position();
|
|
177479
|
+
this.advance();
|
|
177480
|
+
return {
|
|
177481
|
+
kind: "string",
|
|
177482
|
+
value,
|
|
177483
|
+
contentRange: { start: contentStart, end: contentEnd },
|
|
177484
|
+
range: { start, end: this.position() }
|
|
177485
|
+
};
|
|
177486
|
+
}
|
|
177487
|
+
if (char === "\\") {
|
|
177488
|
+
this.advance();
|
|
177489
|
+
value += this.readEscape();
|
|
177490
|
+
continue;
|
|
177491
|
+
}
|
|
177492
|
+
if (char < " ") {
|
|
177493
|
+
this.fail("Unescaped control character in a string.");
|
|
177494
|
+
}
|
|
177495
|
+
this.advance();
|
|
177496
|
+
value += char;
|
|
177497
|
+
}
|
|
177498
|
+
}
|
|
177499
|
+
readEscape() {
|
|
177500
|
+
if (this.atEnd()) {
|
|
177501
|
+
this.fail("Unterminated escape sequence.");
|
|
177502
|
+
}
|
|
177503
|
+
const char = this.text[this.index] ?? "";
|
|
177504
|
+
const simple = ESCAPE_VALUES[char];
|
|
177505
|
+
if (simple !== undefined) {
|
|
177506
|
+
this.advance();
|
|
177507
|
+
return simple;
|
|
177508
|
+
}
|
|
177509
|
+
if (char !== "u") {
|
|
177510
|
+
this.fail(`Unknown escape sequence \`\\${char}\`.`);
|
|
177511
|
+
}
|
|
177512
|
+
this.advance();
|
|
177513
|
+
const digits = this.text.slice(this.index, this.index + 4);
|
|
177514
|
+
if (!/^[0-9a-fA-F]{4}$/.test(digits)) {
|
|
177515
|
+
this.fail("A `\\u` escape needs four hexadecimal digits.");
|
|
177516
|
+
}
|
|
177517
|
+
for (let offset = 0;offset < 4; offset += 1) {
|
|
177518
|
+
this.advance();
|
|
177519
|
+
}
|
|
177520
|
+
return String.fromCharCode(Number.parseInt(digits, 16));
|
|
177521
|
+
}
|
|
177522
|
+
readNumber() {
|
|
177523
|
+
const start = this.position();
|
|
177524
|
+
const pattern = /-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?/y;
|
|
177525
|
+
pattern.lastIndex = this.index;
|
|
177526
|
+
const match = pattern.exec(this.text);
|
|
177527
|
+
if (match === null || match[0].length === 0) {
|
|
177528
|
+
this.fail("Expected a JSON value.");
|
|
177529
|
+
}
|
|
177530
|
+
for (let offset = 0;offset < match[0].length; offset += 1) {
|
|
177531
|
+
this.advance();
|
|
177532
|
+
}
|
|
177533
|
+
if (/[0-9a-zA-Z.+-]/.test(this.peek() ?? "")) {
|
|
177534
|
+
this.fail("Malformed number.");
|
|
177535
|
+
}
|
|
177536
|
+
return { kind: "number", value: Number(match[0]), range: { start, end: this.position() } };
|
|
177537
|
+
}
|
|
177538
|
+
readNull() {
|
|
177539
|
+
const start = this.position();
|
|
177540
|
+
this.consumeWord("null");
|
|
177541
|
+
return { kind: "null", value: null, range: { start, end: this.position() } };
|
|
177542
|
+
}
|
|
177543
|
+
readKeyword(word, value) {
|
|
177544
|
+
const start = this.position();
|
|
177545
|
+
this.consumeWord(word);
|
|
177546
|
+
return { kind: "boolean", value, range: { start, end: this.position() } };
|
|
177547
|
+
}
|
|
177548
|
+
consumeWord(word) {
|
|
177549
|
+
if (this.text.slice(this.index, this.index + word.length) !== word) {
|
|
177550
|
+
this.fail(`Expected \`${word}\`.`);
|
|
177551
|
+
}
|
|
177552
|
+
for (let offset = 0;offset < word.length; offset += 1) {
|
|
177553
|
+
this.advance();
|
|
177554
|
+
}
|
|
177555
|
+
}
|
|
177556
|
+
peek() {
|
|
177557
|
+
return this.text[this.index];
|
|
177558
|
+
}
|
|
177559
|
+
expect(char) {
|
|
177560
|
+
if (this.peek() !== char) {
|
|
177561
|
+
this.fail(`Expected \`${char}\`.`);
|
|
177562
|
+
}
|
|
177563
|
+
this.advance();
|
|
177564
|
+
}
|
|
177565
|
+
advance() {
|
|
177566
|
+
const char = this.text[this.index] ?? "";
|
|
177567
|
+
this.index += 1;
|
|
177568
|
+
if (char === `
|
|
177569
|
+
`) {
|
|
177570
|
+
this.line += 1;
|
|
177571
|
+
this.column = 1;
|
|
177572
|
+
return;
|
|
177573
|
+
}
|
|
177574
|
+
if (char === "\r") {
|
|
177575
|
+
if (this.text[this.index] === `
|
|
177576
|
+
`) {
|
|
177577
|
+
this.index += 1;
|
|
177578
|
+
}
|
|
177579
|
+
this.line += 1;
|
|
177580
|
+
this.column = 1;
|
|
177581
|
+
return;
|
|
177582
|
+
}
|
|
177583
|
+
this.column += 1;
|
|
176918
177584
|
}
|
|
176919
|
-
const moduleDir = dirname2(resolved);
|
|
176920
|
-
return { distRoot: moduleDir, sourceRoot: resolve(moduleDir, "..", "..") };
|
|
176921
|
-
}
|
|
176922
|
-
function sourceRootPath() {
|
|
176923
|
-
return scannerRootsFromModuleUrl(import.meta.url).sourceRoot;
|
|
176924
|
-
}
|
|
176925
|
-
function distRootPath() {
|
|
176926
|
-
return scannerRootsFromModuleUrl(import.meta.url).distRoot;
|
|
176927
|
-
}
|
|
176928
|
-
function scannerExecutableName(language) {
|
|
176929
|
-
return SCANNER_EXECUTABLE_NAMES[language];
|
|
176930
|
-
}
|
|
176931
|
-
function scannerUnavailableDiagnostic2(language, reason) {
|
|
176932
|
-
const label = language.charAt(0).toUpperCase() + language.slice(1);
|
|
176933
|
-
return {
|
|
176934
|
-
severity: "error",
|
|
176935
|
-
code: "code_scanner_unavailable",
|
|
176936
|
-
language,
|
|
176937
|
-
target: language,
|
|
176938
|
-
message: `${label} scanner worker is unavailable: ${reason}`
|
|
176939
|
-
};
|
|
176940
177585
|
}
|
|
176941
177586
|
|
|
176942
|
-
// src/core/
|
|
176943
|
-
var
|
|
176944
|
-
var
|
|
176945
|
-
|
|
176946
|
-
};
|
|
176947
|
-
|
|
176948
|
-
var KNOWN_INCLUDE_KEYS = new Set(["code", "docs"]);
|
|
176949
|
-
var KNOWN_CODE_ENTRY_KEYS = new Set(["patterns", "visibility"]);
|
|
176950
|
-
var LANGUAGE_SUFFIX = {
|
|
176951
|
-
typescript: ".ts",
|
|
176952
|
-
swift: ".swift",
|
|
176953
|
-
dart: ".dart",
|
|
176954
|
-
rust: ".rs"
|
|
176955
|
-
};
|
|
176956
|
-
var LANGUAGE_VISIBILITY = {
|
|
176957
|
-
typescript: ["public", "protected", "private"],
|
|
176958
|
-
swift: ["public", "open", "internal"],
|
|
176959
|
-
dart: ["public"],
|
|
176960
|
-
rust: ["pub", "private"]
|
|
176961
|
-
};
|
|
176962
|
-
function loadConfig(projectRoot) {
|
|
177587
|
+
// src/core/link-manifest.ts
|
|
177588
|
+
var LINK_MANIFEST_FILE_NAME = "docbridge.links.json";
|
|
177589
|
+
var KNOWN_TOP_LEVEL_KEYS2 = new Set(["$schema", "links"]);
|
|
177590
|
+
var KNOWN_ENTRY_KEYS = new Set(["code", "doc", "note"]);
|
|
177591
|
+
var EMPTY_MANIFEST = { entries: [] };
|
|
177592
|
+
function loadLinkManifest(projectRoot) {
|
|
176963
177593
|
let rawText;
|
|
176964
177594
|
try {
|
|
176965
|
-
rawText =
|
|
176966
|
-
} catch {
|
|
177595
|
+
rawText = readFileSync3(join5(projectRoot, LINK_MANIFEST_FILE_NAME), "utf8");
|
|
177596
|
+
} catch (error) {
|
|
177597
|
+
if (!isMissingFileError(error)) {
|
|
177598
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
177599
|
+
return {
|
|
177600
|
+
ok: false,
|
|
177601
|
+
diagnostics: [
|
|
177602
|
+
manifestDiagnostic("config_file_invalid", `Failed to read ${LINK_MANIFEST_FILE_NAME}: ${reason}`)
|
|
177603
|
+
]
|
|
177604
|
+
};
|
|
177605
|
+
}
|
|
176967
177606
|
rawText = undefined;
|
|
176968
177607
|
}
|
|
176969
|
-
|
|
176970
|
-
if (!resolved.ok) {
|
|
176971
|
-
return resolved;
|
|
176972
|
-
}
|
|
176973
|
-
const overlapDiagnostics = detectLanguageOverlap(projectRoot, resolved.config.include.code);
|
|
176974
|
-
if (overlapDiagnostics.length === 0) {
|
|
176975
|
-
return resolved;
|
|
176976
|
-
}
|
|
176977
|
-
return {
|
|
176978
|
-
config: EMPTY_CONFIG,
|
|
176979
|
-
diagnostics: [...resolved.diagnostics, ...overlapDiagnostics],
|
|
176980
|
-
ok: false
|
|
176981
|
-
};
|
|
177608
|
+
return resolveLinkManifest(rawText);
|
|
176982
177609
|
}
|
|
176983
|
-
function
|
|
177610
|
+
function isMissingFileError(error) {
|
|
177611
|
+
return error instanceof Error && error.code === "ENOENT";
|
|
177612
|
+
}
|
|
177613
|
+
function resolveLinkManifest(rawText) {
|
|
176984
177614
|
if (rawText === undefined) {
|
|
176985
|
-
return {
|
|
176986
|
-
config: EMPTY_CONFIG,
|
|
176987
|
-
ok: false,
|
|
176988
|
-
diagnostics: [
|
|
176989
|
-
configDiagnostic("config_file_invalid", CONFIG_FILE_NAME, `${CONFIG_FILE_NAME} was not found. DocBridge requires a configuration file.`)
|
|
176990
|
-
]
|
|
176991
|
-
};
|
|
177615
|
+
return { ok: true, manifest: EMPTY_MANIFEST, diagnostics: [] };
|
|
176992
177616
|
}
|
|
176993
|
-
|
|
176994
|
-
|
|
176995
|
-
parsed = JSON.parse(rawText);
|
|
176996
|
-
} catch (error) {
|
|
176997
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
177617
|
+
const parsed = parseJsonSource(rawText);
|
|
177618
|
+
if (!parsed.ok) {
|
|
176998
177619
|
return {
|
|
176999
|
-
config: EMPTY_CONFIG,
|
|
177000
177620
|
ok: false,
|
|
177001
177621
|
diagnostics: [
|
|
177002
|
-
|
|
177622
|
+
manifestDiagnostic("config_file_invalid", `Failed to parse ${LINK_MANIFEST_FILE_NAME}: ${parsed.message}`, parsed.position)
|
|
177003
177623
|
]
|
|
177004
177624
|
};
|
|
177005
177625
|
}
|
|
177006
|
-
|
|
177007
|
-
|
|
177008
|
-
|
|
177009
|
-
|
|
177626
|
+
return validateManifest(parsed.root);
|
|
177627
|
+
}
|
|
177628
|
+
function validateManifest(root) {
|
|
177629
|
+
if (root.kind !== "object") {
|
|
177630
|
+
return { ok: false, diagnostics: [invalidValue("", "an object", root)] };
|
|
177010
177631
|
}
|
|
177011
|
-
|
|
177012
|
-
|
|
177013
|
-
|
|
177632
|
+
const diagnostics = [];
|
|
177633
|
+
for (const member of root.members) {
|
|
177634
|
+
if (!KNOWN_TOP_LEVEL_KEYS2.has(member.key)) {
|
|
177635
|
+
diagnostics.push(unknownKey(member.key, member));
|
|
177014
177636
|
}
|
|
177015
177637
|
}
|
|
177016
|
-
|
|
177017
|
-
|
|
177638
|
+
reportDuplicateKeys(root, "", diagnostics);
|
|
177639
|
+
const links = root.members.find((member) => member.key === "links");
|
|
177640
|
+
if (links === undefined) {
|
|
177641
|
+
return {
|
|
177642
|
+
ok: false,
|
|
177643
|
+
diagnostics: [...diagnostics, invalidValue("links", "present", root)]
|
|
177644
|
+
};
|
|
177018
177645
|
}
|
|
177019
|
-
|
|
177020
|
-
|
|
177021
|
-
|
|
177022
|
-
|
|
177646
|
+
if (links.value.kind !== "array") {
|
|
177647
|
+
return {
|
|
177648
|
+
ok: false,
|
|
177649
|
+
diagnostics: [...diagnostics, invalidValue("links", "an array", links.value)]
|
|
177650
|
+
};
|
|
177023
177651
|
}
|
|
177024
|
-
|
|
177025
|
-
|
|
177026
|
-
|
|
177652
|
+
const entries = [];
|
|
177653
|
+
for (const [index, element] of links.value.elements.entries()) {
|
|
177654
|
+
const outcome = readEntry(element, index);
|
|
177655
|
+
diagnostics.push(...outcome.diagnostics);
|
|
177656
|
+
if (outcome.entry !== undefined) {
|
|
177657
|
+
entries.push(outcome.entry);
|
|
177027
177658
|
}
|
|
177028
177659
|
}
|
|
177029
|
-
|
|
177030
|
-
|
|
177031
|
-
const ok = diagnostics.length === 0;
|
|
177032
|
-
const config = ok ? { include: { code, docs: include.docs } } : EMPTY_CONFIG;
|
|
177033
|
-
return { config, diagnostics, ok };
|
|
177034
|
-
}
|
|
177035
|
-
function validateCodeInclude(value, diagnostics) {
|
|
177036
|
-
if (Array.isArray(value)) {
|
|
177037
|
-
diagnostics.push(configDiagnostic("config_invalid_value", "include.code", '`include.code` must be a language-keyed object such as `{ "typescript": { "patterns": ["src/**/*.ts"] } }`, not an array.'));
|
|
177038
|
-
return {};
|
|
177039
|
-
}
|
|
177040
|
-
if (!isPlainObject(value)) {
|
|
177041
|
-
diagnostics.push(configDiagnostic("config_invalid_value", "include.code", "`include.code` must be a language-keyed object."));
|
|
177042
|
-
return {};
|
|
177043
|
-
}
|
|
177044
|
-
const keys = Object.keys(value);
|
|
177045
|
-
if (keys.length === 0) {
|
|
177046
|
-
diagnostics.push(configDiagnostic("config_invalid_value", "include.code", "`include.code` must configure at least one language."));
|
|
177047
|
-
return {};
|
|
177660
|
+
if (diagnostics.some(isBlocking)) {
|
|
177661
|
+
return { ok: false, diagnostics };
|
|
177048
177662
|
}
|
|
177049
|
-
|
|
177050
|
-
for (const key of keys) {
|
|
177051
|
-
if (!isCodeLanguage(key)) {
|
|
177052
|
-
diagnostics.push(configDiagnostic("config_invalid_value", `include.code.${key}`, `Unknown code language: ${key}. Supported languages: ${KNOWN_CODE_LANGUAGES.join(", ")}.`));
|
|
177053
|
-
continue;
|
|
177054
|
-
}
|
|
177055
|
-
const entry = validateCodeEntry(key, value[key], diagnostics);
|
|
177056
|
-
if (entry !== undefined) {
|
|
177057
|
-
result[key] = entry;
|
|
177058
|
-
}
|
|
177059
|
-
}
|
|
177060
|
-
return result;
|
|
177663
|
+
return { ok: true, manifest: { entries }, diagnostics };
|
|
177061
177664
|
}
|
|
177062
|
-
function
|
|
177063
|
-
const
|
|
177064
|
-
if (
|
|
177065
|
-
|
|
177066
|
-
return;
|
|
177067
|
-
}
|
|
177068
|
-
for (const key of Object.keys(value)) {
|
|
177069
|
-
if (!KNOWN_CODE_ENTRY_KEYS.has(key)) {
|
|
177070
|
-
diagnostics.push(configDiagnostic("config_unknown_key", `${target}.${key}`, `Unknown configuration key under \`${target}\`: ${key}`));
|
|
177071
|
-
}
|
|
177072
|
-
}
|
|
177073
|
-
const before = diagnostics.length;
|
|
177074
|
-
validatePatternArray(value.patterns, `code.${language}.patterns`, LANGUAGE_SUFFIX[language], language === "typescript", diagnostics);
|
|
177075
|
-
if ("visibility" in value) {
|
|
177076
|
-
if (!isStringArray(value.visibility)) {
|
|
177077
|
-
diagnostics.push(configDiagnostic("config_invalid_value", `${target}.visibility`, `\`${target}.visibility\` must be an array of strings.`));
|
|
177078
|
-
} else {
|
|
177079
|
-
validateVisibilityOptions(language, value.visibility, target, diagnostics);
|
|
177080
|
-
}
|
|
177081
|
-
}
|
|
177082
|
-
if (diagnostics.length !== before) {
|
|
177083
|
-
return;
|
|
177665
|
+
function readEntry(element, index) {
|
|
177666
|
+
const path = `links[${index}]`;
|
|
177667
|
+
if (element.kind !== "object") {
|
|
177668
|
+
return { diagnostics: [invalidValue(path, "an object", element)] };
|
|
177084
177669
|
}
|
|
177085
|
-
const
|
|
177086
|
-
|
|
177087
|
-
|
|
177670
|
+
const diagnostics = [];
|
|
177671
|
+
for (const member of element.members) {
|
|
177672
|
+
if (!KNOWN_ENTRY_KEYS.has(member.key)) {
|
|
177673
|
+
diagnostics.push(unknownKey(`${path}.${member.key}`, member));
|
|
177674
|
+
}
|
|
177675
|
+
}
|
|
177676
|
+
reportDuplicateKeys(element, `${path}.`, diagnostics);
|
|
177677
|
+
const code = readString(element, "code", path, diagnostics);
|
|
177678
|
+
const doc = readString(element, "doc", path, diagnostics);
|
|
177679
|
+
const note = readOptionalNote(element, path, diagnostics);
|
|
177680
|
+
if (code === undefined || doc === undefined || diagnostics.some(isBlocking)) {
|
|
177681
|
+
return { diagnostics };
|
|
177682
|
+
}
|
|
177683
|
+
const codeTarget = parseTarget(code, diagnostics);
|
|
177684
|
+
const docTarget = parseTarget(doc, diagnostics);
|
|
177685
|
+
if (codeTarget === undefined || docTarget === undefined) {
|
|
177686
|
+
return { diagnostics };
|
|
177687
|
+
}
|
|
177688
|
+
const entry = {
|
|
177689
|
+
code: codeTarget,
|
|
177690
|
+
doc: docTarget,
|
|
177691
|
+
codeEndpoint: code.value,
|
|
177692
|
+
docEndpoint: doc.value,
|
|
177693
|
+
location: positionOf(element.range),
|
|
177694
|
+
range: element.range,
|
|
177695
|
+
codeRange: code.contentRange,
|
|
177696
|
+
docRange: doc.contentRange
|
|
177697
|
+
};
|
|
177698
|
+
if (note !== undefined) {
|
|
177699
|
+
entry.note = note;
|
|
177088
177700
|
}
|
|
177089
|
-
return entry;
|
|
177701
|
+
return { entry, diagnostics };
|
|
177090
177702
|
}
|
|
177091
|
-
function
|
|
177092
|
-
const
|
|
177093
|
-
if (
|
|
177094
|
-
diagnostics.push(
|
|
177703
|
+
function readString(entry, key, path, diagnostics) {
|
|
177704
|
+
const member = entry.members.find((candidate) => candidate.key === key);
|
|
177705
|
+
if (member === undefined) {
|
|
177706
|
+
diagnostics.push(invalidValue(`${path}.${key}`, "present", entry));
|
|
177095
177707
|
return;
|
|
177096
177708
|
}
|
|
177097
|
-
|
|
177098
|
-
|
|
177099
|
-
|
|
177100
|
-
}
|
|
177709
|
+
if (member.value.kind !== "string") {
|
|
177710
|
+
diagnostics.push(invalidValue(`${path}.${key}`, "a string", member.value));
|
|
177711
|
+
return;
|
|
177101
177712
|
}
|
|
177713
|
+
return member.value;
|
|
177102
177714
|
}
|
|
177103
|
-
function
|
|
177104
|
-
const
|
|
177105
|
-
if (
|
|
177106
|
-
diagnostics.push(configDiagnostic("config_invalid_value", target, `\`${target}\` must be a non-empty array of strings.`));
|
|
177715
|
+
function readOptionalNote(entry, path, diagnostics) {
|
|
177716
|
+
const member = entry.members.find((candidate) => candidate.key === "note");
|
|
177717
|
+
if (member === undefined) {
|
|
177107
177718
|
return;
|
|
177108
177719
|
}
|
|
177109
|
-
if (value.
|
|
177110
|
-
diagnostics.push(
|
|
177720
|
+
if (member.value.kind !== "string") {
|
|
177721
|
+
diagnostics.push(invalidValue(`${path}.note`, "a string", member.value));
|
|
177111
177722
|
return;
|
|
177112
177723
|
}
|
|
177113
|
-
|
|
177114
|
-
if (typeof pattern !== "string") {
|
|
177115
|
-
diagnostics.push(configDiagnostic("config_invalid_value", target, `\`${target}\` patterns must be strings.`));
|
|
177116
|
-
continue;
|
|
177117
|
-
}
|
|
177118
|
-
const globResult = validateGlobPattern(pattern);
|
|
177119
|
-
if (!globResult.ok) {
|
|
177120
|
-
diagnostics.push(configDiagnostic("config_invalid_value", pattern, `Invalid pattern in \`${target}\`: ${globResult.message}`));
|
|
177121
|
-
continue;
|
|
177122
|
-
}
|
|
177123
|
-
const suffixError = checkSuffix(pattern, requiredSuffix, excludeDeclarationFiles);
|
|
177124
|
-
if (suffixError !== undefined) {
|
|
177125
|
-
diagnostics.push(configDiagnostic("config_invalid_value", pattern, suffixError));
|
|
177126
|
-
}
|
|
177127
|
-
}
|
|
177724
|
+
return member.value.value;
|
|
177128
177725
|
}
|
|
177129
|
-
function
|
|
177130
|
-
|
|
177131
|
-
|
|
177132
|
-
|
|
177133
|
-
|
|
177134
|
-
|
|
177726
|
+
function parseTarget(node, diagnostics) {
|
|
177727
|
+
const result = parseLinkTarget(node.value, {
|
|
177728
|
+
location: positionOf(node.contentRange),
|
|
177729
|
+
targetRange: node.contentRange
|
|
177730
|
+
});
|
|
177731
|
+
if (result.ok) {
|
|
177732
|
+
return result.target;
|
|
177135
177733
|
}
|
|
177734
|
+
diagnostics.push(result.diagnostic);
|
|
177136
177735
|
return;
|
|
177137
177736
|
}
|
|
177138
|
-
function
|
|
177139
|
-
const
|
|
177140
|
-
for (const
|
|
177141
|
-
if (
|
|
177142
|
-
diagnostics.push(
|
|
177737
|
+
function reportDuplicateKeys(object, pathPrefix, diagnostics) {
|
|
177738
|
+
const seen = new Set;
|
|
177739
|
+
for (const member of object.members) {
|
|
177740
|
+
if (seen.has(member.key)) {
|
|
177741
|
+
diagnostics.push(manifestDiagnostic("config_invalid_value", `Duplicate key ${pathPrefix}${member.key} in ${LINK_MANIFEST_FILE_NAME}.`, positionOf(member.keyRange), member.keyRange));
|
|
177143
177742
|
}
|
|
177743
|
+
seen.add(member.key);
|
|
177144
177744
|
}
|
|
177145
|
-
return diagnostics;
|
|
177146
177745
|
}
|
|
177147
|
-
function
|
|
177148
|
-
return
|
|
177149
|
-
}
|
|
177150
|
-
function isPlainObject(value) {
|
|
177151
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
177746
|
+
function isBlocking(diagnostic) {
|
|
177747
|
+
return diagnostic.code !== "invalid_link_target";
|
|
177152
177748
|
}
|
|
177153
|
-
function
|
|
177154
|
-
return
|
|
177749
|
+
function unknownKey(path, member) {
|
|
177750
|
+
return manifestDiagnostic("config_unknown_key", `Unknown key ${path} in ${LINK_MANIFEST_FILE_NAME}.`, positionOf(member.keyRange), member.keyRange);
|
|
177155
177751
|
}
|
|
177156
|
-
|
|
177157
|
-
|
|
177158
|
-
|
|
177159
|
-
return [...diagnostics].toSorted(compareDiagnostics);
|
|
177752
|
+
function invalidValue(path, expectation, node) {
|
|
177753
|
+
const subject = path === "" ? LINK_MANIFEST_FILE_NAME : path;
|
|
177754
|
+
return manifestDiagnostic("config_invalid_value", `${subject} must be ${expectation}.`, positionOf(node.range), node.range);
|
|
177160
177755
|
}
|
|
177161
|
-
function
|
|
177162
|
-
const
|
|
177163
|
-
|
|
177164
|
-
|
|
177165
|
-
|
|
177166
|
-
|
|
177167
|
-
|
|
177168
|
-
|
|
177756
|
+
function manifestDiagnostic(code, message, position, range) {
|
|
177757
|
+
const diagnostic = {
|
|
177758
|
+
severity: "error",
|
|
177759
|
+
code,
|
|
177760
|
+
target: LINK_MANIFEST_FILE_NAME,
|
|
177761
|
+
message
|
|
177762
|
+
};
|
|
177763
|
+
if (position !== undefined) {
|
|
177764
|
+
diagnostic.location = {
|
|
177765
|
+
filePath: LINK_MANIFEST_FILE_NAME,
|
|
177766
|
+
line: position.line,
|
|
177767
|
+
column: position.column
|
|
177768
|
+
};
|
|
177169
177769
|
}
|
|
177170
|
-
|
|
177171
|
-
|
|
177172
|
-
function formatDiagnostic(diagnostic) {
|
|
177173
|
-
const location = diagnostic.location;
|
|
177174
|
-
if (location === undefined) {
|
|
177175
|
-
return `${diagnostic.target} ${diagnostic.severity} ${diagnostic.code} - ${diagnostic.message}`;
|
|
177770
|
+
if (range !== undefined) {
|
|
177771
|
+
diagnostic.range = range;
|
|
177176
177772
|
}
|
|
177177
|
-
|
|
177178
|
-
return `${prefix} ${diagnostic.severity} ${diagnostic.code} ${diagnostic.target} - ${diagnostic.message}`;
|
|
177773
|
+
return diagnostic;
|
|
177179
177774
|
}
|
|
177180
|
-
function
|
|
177181
|
-
return
|
|
177775
|
+
function positionOf(range) {
|
|
177776
|
+
return {
|
|
177777
|
+
filePath: LINK_MANIFEST_FILE_NAME,
|
|
177778
|
+
line: range.start.line,
|
|
177779
|
+
column: range.start.column
|
|
177780
|
+
};
|
|
177182
177781
|
}
|
|
177183
|
-
|
|
177184
|
-
|
|
177185
|
-
|
|
177186
|
-
|
|
177187
|
-
|
|
177188
|
-
|
|
177189
|
-
|
|
177190
|
-
|
|
177782
|
+
|
|
177783
|
+
// src/core/suggest.ts
|
|
177784
|
+
function nearestMatch(input, candidates) {
|
|
177785
|
+
const ranked = candidates.map((candidate, index) => ({
|
|
177786
|
+
candidate,
|
|
177787
|
+
distance: editDistance(input, candidate),
|
|
177788
|
+
index
|
|
177789
|
+
})).toSorted((left, right) => left.distance - right.distance || left.index - right.index);
|
|
177790
|
+
const abbreviation = ranked.find(({ candidate }) => isOrderedAbbreviation(input, candidate));
|
|
177791
|
+
if (abbreviation !== undefined) {
|
|
177792
|
+
return abbreviation.candidate;
|
|
177191
177793
|
}
|
|
177192
|
-
|
|
177193
|
-
|
|
177794
|
+
const best = ranked[0];
|
|
177795
|
+
if (best === undefined) {
|
|
177796
|
+
return;
|
|
177194
177797
|
}
|
|
177195
|
-
|
|
177196
|
-
|
|
177197
|
-
function compareString(left, right) {
|
|
177198
|
-
return left.localeCompare(right);
|
|
177798
|
+
const closeEnough = best.distance <= Math.max(1, Math.floor(best.candidate.length / 2));
|
|
177799
|
+
return closeEnough ? best.candidate : undefined;
|
|
177199
177800
|
}
|
|
177200
|
-
function
|
|
177201
|
-
|
|
177801
|
+
function isOrderedAbbreviation(input, candidate) {
|
|
177802
|
+
if (input.length < 3 || input.length >= candidate.length) {
|
|
177803
|
+
return false;
|
|
177804
|
+
}
|
|
177805
|
+
let candidateIndex = 0;
|
|
177806
|
+
for (const character of input) {
|
|
177807
|
+
const matchIndex = candidate.indexOf(character, candidateIndex);
|
|
177808
|
+
if (matchIndex === -1) {
|
|
177809
|
+
return false;
|
|
177810
|
+
}
|
|
177811
|
+
candidateIndex = matchIndex + 1;
|
|
177812
|
+
}
|
|
177813
|
+
return true;
|
|
177202
177814
|
}
|
|
177203
|
-
function
|
|
177204
|
-
|
|
177815
|
+
function editDistance(left, right) {
|
|
177816
|
+
let previous = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
177817
|
+
for (let leftIndex = 1;leftIndex <= left.length; leftIndex += 1) {
|
|
177818
|
+
const current = [leftIndex];
|
|
177819
|
+
for (let rightIndex = 1;rightIndex <= right.length; rightIndex += 1) {
|
|
177820
|
+
const substitutionCost = left[leftIndex - 1] === right[rightIndex - 1] ? 0 : 1;
|
|
177821
|
+
current[rightIndex] = Math.min((current[rightIndex - 1] ?? Number.POSITIVE_INFINITY) + 1, (previous[rightIndex] ?? Number.POSITIVE_INFINITY) + 1, (previous[rightIndex - 1] ?? Number.POSITIVE_INFINITY) + substitutionCost);
|
|
177822
|
+
}
|
|
177823
|
+
previous = current;
|
|
177824
|
+
}
|
|
177825
|
+
return previous[right.length] ?? 0;
|
|
177205
177826
|
}
|
|
177206
177827
|
|
|
177207
|
-
// src/core/
|
|
177208
|
-
function
|
|
177209
|
-
|
|
177210
|
-
|
|
177211
|
-
}
|
|
177212
|
-
function fragmentOf(endpoint) {
|
|
177213
|
-
const hashIndex = endpoint.indexOf("#");
|
|
177214
|
-
return hashIndex === -1 ? endpoint : endpoint.slice(hashIndex + 1);
|
|
177215
|
-
}
|
|
177216
|
-
function compareEndpointOrder(left, right) {
|
|
177217
|
-
return left.filePath.localeCompare(right.filePath) || left.line - right.line || left.column - right.column || left.endpoint.localeCompare(right.endpoint);
|
|
177218
|
-
}
|
|
177219
|
-
function endpointRange(element) {
|
|
177220
|
-
const range = element.kind === "code" ? element.nameRange : element.headingTextRange;
|
|
177221
|
-
if (range !== undefined) {
|
|
177222
|
-
return range;
|
|
177828
|
+
// src/core/link-manifest-apply.ts
|
|
177829
|
+
function applyLinkManifest(input) {
|
|
177830
|
+
if (input.manifest.entries.length === 0) {
|
|
177831
|
+
return { codeFiles: input.codeFiles, docFiles: input.docFiles, diagnostics: [] };
|
|
177223
177832
|
}
|
|
177224
|
-
const
|
|
177225
|
-
|
|
177833
|
+
const state = new ApplyState(input);
|
|
177834
|
+
for (const entry of input.manifest.entries) {
|
|
177835
|
+
state.applyEntry(entry);
|
|
177836
|
+
}
|
|
177837
|
+
return state.result();
|
|
177226
177838
|
}
|
|
177227
177839
|
|
|
177228
|
-
|
|
177229
|
-
|
|
177230
|
-
|
|
177231
|
-
|
|
177232
|
-
|
|
177233
|
-
|
|
177234
|
-
|
|
177235
|
-
|
|
177840
|
+
class ApplyState {
|
|
177841
|
+
erroredFiles;
|
|
177842
|
+
codeStates = new Map;
|
|
177843
|
+
docStates = new Map;
|
|
177844
|
+
codeOrder;
|
|
177845
|
+
docOrder;
|
|
177846
|
+
diagnostics = [];
|
|
177847
|
+
constructor(input) {
|
|
177848
|
+
this.erroredFiles = collectErroredFiles(input.scanDiagnostics);
|
|
177849
|
+
this.codeOrder = input.codeFiles;
|
|
177850
|
+
this.docOrder = input.docFiles;
|
|
177851
|
+
for (const file of input.codeFiles) {
|
|
177852
|
+
const byEndpoint = new Map;
|
|
177853
|
+
for (const symbol of [...file.symbols, ...file.undocumentedSymbols]) {
|
|
177854
|
+
if (!byEndpoint.has(symbol.endpoint)) {
|
|
177855
|
+
byEndpoint.set(symbol.endpoint, symbol);
|
|
177856
|
+
}
|
|
177857
|
+
}
|
|
177858
|
+
this.codeStates.set(file.filePath, {
|
|
177859
|
+
file,
|
|
177860
|
+
symbols: file.symbols,
|
|
177861
|
+
undocumentedSymbols: file.undocumentedSymbols,
|
|
177862
|
+
links: file.links,
|
|
177863
|
+
byEndpoint,
|
|
177864
|
+
outgoing: new Set(file.links.map((annotation) => pairKey(annotation.source, annotation.target))),
|
|
177865
|
+
changed: false
|
|
177866
|
+
});
|
|
177867
|
+
}
|
|
177868
|
+
for (const file of input.docFiles) {
|
|
177869
|
+
this.docStates.set(file.filePath, {
|
|
177870
|
+
file,
|
|
177871
|
+
headings: file.headings,
|
|
177872
|
+
links: file.links,
|
|
177873
|
+
anchors: new Set(file.anchors.map((anchor) => anchor.endpoint)),
|
|
177874
|
+
outgoing: new Set(file.links.map((annotation) => pairKey(annotation.source, annotation.target))),
|
|
177875
|
+
changed: false
|
|
177876
|
+
});
|
|
177236
177877
|
}
|
|
177237
177878
|
}
|
|
177238
|
-
|
|
177239
|
-
|
|
177240
|
-
|
|
177241
|
-
if (!docByEndpoint.has(anchor.endpoint)) {
|
|
177242
|
-
docByEndpoint.set(anchor.endpoint, anchor);
|
|
177243
|
-
}
|
|
177879
|
+
applyEntry(entry) {
|
|
177880
|
+
if (this.erroredFiles.has(entry.code.filePath) || this.erroredFiles.has(entry.doc.filePath)) {
|
|
177881
|
+
return;
|
|
177244
177882
|
}
|
|
177883
|
+
const codeResolved = this.resolveCodeSide(entry);
|
|
177884
|
+
const docResolved = this.resolveDocSide(entry);
|
|
177885
|
+
if (!codeResolved || !docResolved) {
|
|
177886
|
+
return;
|
|
177887
|
+
}
|
|
177888
|
+
this.addLinks(entry);
|
|
177245
177889
|
}
|
|
177246
|
-
|
|
177247
|
-
|
|
177248
|
-
|
|
177249
|
-
|
|
177250
|
-
|
|
177251
|
-
for (const file of codeFiles) {
|
|
177252
|
-
for (const link of file.links) {
|
|
177253
|
-
if (codeByEndpoint.has(link.source) && docByEndpoint.has(link.target)) {
|
|
177254
|
-
addEdge(link.source, link.target);
|
|
177255
|
-
}
|
|
177890
|
+
resolveCodeSide(entry) {
|
|
177891
|
+
const state = this.codeStates.get(entry.code.filePath);
|
|
177892
|
+
if (state === undefined) {
|
|
177893
|
+
this.report("code_file_not_found", entry.codeEndpoint, `Code file ${entry.code.filePath} declared in the link manifest is not in the managed code set.`, entry, "code");
|
|
177894
|
+
return false;
|
|
177256
177895
|
}
|
|
177896
|
+
const symbol = state.byEndpoint.get(entry.codeEndpoint);
|
|
177897
|
+
if (symbol === undefined) {
|
|
177898
|
+
this.report("code_symbol_not_found", entry.codeEndpoint, this.symbolNotFoundMessage(entry, state), entry, "code", state.file.language);
|
|
177899
|
+
return false;
|
|
177900
|
+
}
|
|
177901
|
+
this.markDocumented(state, symbol);
|
|
177902
|
+
return true;
|
|
177257
177903
|
}
|
|
177258
|
-
|
|
177259
|
-
|
|
177260
|
-
|
|
177261
|
-
|
|
177262
|
-
|
|
177904
|
+
resolveDocSide(entry) {
|
|
177905
|
+
const state = this.docStates.get(entry.doc.filePath);
|
|
177906
|
+
if (state === undefined) {
|
|
177907
|
+
this.report("doc_file_not_found", entry.docEndpoint, `Doc file ${entry.doc.filePath} declared in the link manifest is not in the managed docs set.`, entry, "doc");
|
|
177908
|
+
return false;
|
|
177263
177909
|
}
|
|
177910
|
+
if (!state.anchors.has(entry.docEndpoint)) {
|
|
177911
|
+
this.report("doc_anchor_not_found", entry.docEndpoint, `Doc anchor ${entry.docEndpoint} declared in the link manifest does not exist.`, entry, "doc");
|
|
177912
|
+
return false;
|
|
177913
|
+
}
|
|
177914
|
+
this.markAnnotated(state, entry.docEndpoint);
|
|
177915
|
+
return true;
|
|
177264
177916
|
}
|
|
177265
|
-
|
|
177266
|
-
|
|
177267
|
-
|
|
177268
|
-
|
|
177269
|
-
|
|
177270
|
-
|
|
177917
|
+
addLinks(entry) {
|
|
177918
|
+
const codeState = this.codeStates.get(entry.code.filePath);
|
|
177919
|
+
const docState = this.docStates.get(entry.doc.filePath);
|
|
177920
|
+
if (codeState === undefined || docState === undefined) {
|
|
177921
|
+
return;
|
|
177922
|
+
}
|
|
177923
|
+
const forward = pairKey(entry.codeEndpoint, entry.docEndpoint);
|
|
177924
|
+
const backward = pairKey(entry.docEndpoint, entry.codeEndpoint);
|
|
177925
|
+
const hasForward = codeState.outgoing.has(forward);
|
|
177926
|
+
const hasBackward = docState.outgoing.has(backward);
|
|
177927
|
+
if (hasForward || hasBackward) {
|
|
177928
|
+
this.diagnostics.push({
|
|
177929
|
+
severity: "warning",
|
|
177930
|
+
code: "duplicate_link",
|
|
177931
|
+
source: entry.codeEndpoint,
|
|
177932
|
+
target: entry.docEndpoint,
|
|
177933
|
+
message: `Link ${entry.codeEndpoint} to ${entry.docEndpoint} is already declared.`,
|
|
177934
|
+
location: entry.location,
|
|
177935
|
+
range: entry.range
|
|
177936
|
+
});
|
|
177937
|
+
}
|
|
177938
|
+
if (!hasForward) {
|
|
177939
|
+
codeState.links = [
|
|
177940
|
+
...codeState.links,
|
|
177941
|
+
manifestLink(entry.codeEndpoint, entry.docEndpoint, entry, "doc")
|
|
177942
|
+
];
|
|
177943
|
+
codeState.outgoing.add(forward);
|
|
177944
|
+
codeState.changed = true;
|
|
177945
|
+
}
|
|
177946
|
+
if (!hasBackward) {
|
|
177947
|
+
docState.links = [
|
|
177948
|
+
...docState.links,
|
|
177949
|
+
manifestLink(entry.docEndpoint, entry.codeEndpoint, entry, "code")
|
|
177950
|
+
];
|
|
177951
|
+
docState.outgoing.add(backward);
|
|
177952
|
+
docState.changed = true;
|
|
177953
|
+
}
|
|
177271
177954
|
}
|
|
177272
|
-
|
|
177273
|
-
|
|
177274
|
-
|
|
177275
|
-
if (code !== undefined) {
|
|
177276
|
-
resolved.push(code);
|
|
177277
|
-
continue;
|
|
177955
|
+
markDocumented(state, symbol) {
|
|
177956
|
+
if (!state.undocumentedSymbols.includes(symbol)) {
|
|
177957
|
+
return;
|
|
177278
177958
|
}
|
|
177279
|
-
|
|
177280
|
-
|
|
177281
|
-
|
|
177959
|
+
state.undocumentedSymbols = state.undocumentedSymbols.filter((candidate) => candidate !== symbol);
|
|
177960
|
+
state.symbols = [...state.symbols, symbol];
|
|
177961
|
+
state.changed = true;
|
|
177962
|
+
}
|
|
177963
|
+
markAnnotated(state, endpoint) {
|
|
177964
|
+
const index = state.headings.findIndex((heading2) => heading2.anchor?.endpoint === endpoint);
|
|
177965
|
+
const heading = state.headings[index];
|
|
177966
|
+
if (heading === undefined || heading.hasCodeAnnotation) {
|
|
177967
|
+
return;
|
|
177968
|
+
}
|
|
177969
|
+
state.headings = state.headings.toSpliced(index, 1, {
|
|
177970
|
+
...heading,
|
|
177971
|
+
hasCodeAnnotation: true
|
|
177972
|
+
});
|
|
177973
|
+
state.changed = true;
|
|
177974
|
+
}
|
|
177975
|
+
symbolNotFoundMessage(entry, state) {
|
|
177976
|
+
const base = `Code symbol ${entry.codeEndpoint} declared in the link manifest does not exist.`;
|
|
177977
|
+
const candidates = [...state.byEndpoint.values()].map((symbol) => symbol.canonicalId);
|
|
177978
|
+
const suggestion = nearestMatch(entry.code.fragment, candidates);
|
|
177979
|
+
return suggestion === undefined ? base : `${base} Did you mean \`${suggestion}\`?`;
|
|
177980
|
+
}
|
|
177981
|
+
report(code, target, message, entry, side, language) {
|
|
177982
|
+
const range = side === "code" ? entry.codeRange : entry.docRange;
|
|
177983
|
+
const diagnostic = {
|
|
177984
|
+
severity: "error",
|
|
177985
|
+
code,
|
|
177986
|
+
target,
|
|
177987
|
+
message,
|
|
177988
|
+
location: { ...entry.location, line: range.start.line, column: range.start.column },
|
|
177989
|
+
range
|
|
177990
|
+
};
|
|
177991
|
+
if (language !== undefined) {
|
|
177992
|
+
diagnostic.language = language;
|
|
177282
177993
|
}
|
|
177994
|
+
this.diagnostics.push(diagnostic);
|
|
177283
177995
|
}
|
|
177284
|
-
|
|
177285
|
-
|
|
177286
|
-
|
|
177287
|
-
|
|
177288
|
-
|
|
177289
|
-
|
|
177290
|
-
|
|
177996
|
+
result() {
|
|
177997
|
+
const codeFiles = this.codeOrder.map((file) => {
|
|
177998
|
+
const state = this.codeStates.get(file.filePath);
|
|
177999
|
+
if (state === undefined || !state.changed) {
|
|
178000
|
+
return file;
|
|
178001
|
+
}
|
|
178002
|
+
return {
|
|
178003
|
+
...file,
|
|
178004
|
+
symbols: state.symbols,
|
|
178005
|
+
undocumentedSymbols: state.undocumentedSymbols,
|
|
178006
|
+
links: state.links
|
|
178007
|
+
};
|
|
178008
|
+
});
|
|
178009
|
+
const docFiles = this.docOrder.map((file) => {
|
|
178010
|
+
const state = this.docStates.get(file.filePath);
|
|
178011
|
+
if (state === undefined || !state.changed) {
|
|
178012
|
+
return file;
|
|
178013
|
+
}
|
|
178014
|
+
return { ...file, headings: state.headings, links: state.links };
|
|
178015
|
+
});
|
|
178016
|
+
return { codeFiles, docFiles, diagnostics: this.diagnostics };
|
|
177291
178017
|
}
|
|
177292
|
-
existing.add(value);
|
|
177293
178018
|
}
|
|
177294
|
-
function
|
|
177295
|
-
return
|
|
178019
|
+
function pairKey(source, target) {
|
|
178020
|
+
return `${source} -> ${target}`;
|
|
178021
|
+
}
|
|
178022
|
+
function manifestLink(source, target, entry, side) {
|
|
178023
|
+
return {
|
|
178024
|
+
source,
|
|
178025
|
+
target,
|
|
178026
|
+
location: entry.location,
|
|
178027
|
+
targetRange: side === "code" ? entry.codeRange : entry.docRange
|
|
178028
|
+
};
|
|
177296
178029
|
}
|
|
177297
178030
|
|
|
177298
178031
|
// src/core/markdown-syntax.ts
|
|
@@ -177565,10 +178298,17 @@ function scanProject(options) {
|
|
|
177565
178298
|
if (!configResult.ok) {
|
|
177566
178299
|
return { ok: false, diagnostics: configResult.diagnostics };
|
|
177567
178300
|
}
|
|
178301
|
+
const manifestResult = loadLinkManifest(options.projectRoot);
|
|
178302
|
+
if (!manifestResult.ok) {
|
|
178303
|
+
return { ok: false, diagnostics: [...configResult.diagnostics, ...manifestResult.diagnostics] };
|
|
178304
|
+
}
|
|
177568
178305
|
const collectCode = options.collectCode ?? collectCodeFiles;
|
|
177569
178306
|
const collectDocs = options.collectDocs ?? collectFiles;
|
|
177570
178307
|
const readFile = options.readFile ?? ((relPath) => readManagedFile(options.projectRoot, relPath));
|
|
177571
|
-
const diagnostics = [
|
|
178308
|
+
const diagnostics = [
|
|
178309
|
+
...configResult.diagnostics,
|
|
178310
|
+
...manifestResult.diagnostics
|
|
178311
|
+
];
|
|
177572
178312
|
const contentByFile = options.keepContent ? new Map : undefined;
|
|
177573
178313
|
const codeScan = scanCodeFiles(options.projectRoot, collectCode(options.projectRoot, configResult.config.include.code), configResult.config.include.code, readFile, contentByFile === undefined ? undefined : (relPath, content) => contentByFile.set(relPath, content));
|
|
177574
178314
|
diagnostics.push(...codeScan.diagnostics);
|
|
@@ -177584,17 +178324,24 @@ function scanProject(options) {
|
|
|
177584
178324
|
diagnostics.push(...scan2.diagnostics);
|
|
177585
178325
|
docFiles.push(scan2);
|
|
177586
178326
|
}
|
|
177587
|
-
const
|
|
178327
|
+
const applied = applyLinkManifest({
|
|
178328
|
+
manifest: manifestResult.manifest,
|
|
178329
|
+
codeFiles: codeScan.codeFiles,
|
|
178330
|
+
docFiles,
|
|
178331
|
+
scanDiagnostics: diagnostics
|
|
178332
|
+
});
|
|
178333
|
+
diagnostics.push(...applied.diagnostics);
|
|
178334
|
+
const codeFiles = applied.codeFiles;
|
|
177588
178335
|
const scan = {
|
|
177589
178336
|
codeFiles,
|
|
177590
|
-
docFiles,
|
|
178337
|
+
docFiles: applied.docFiles,
|
|
177591
178338
|
diagnostics
|
|
177592
178339
|
};
|
|
177593
178340
|
if (contentByFile !== undefined) {
|
|
177594
178341
|
scan.contentByFile = contentByFile;
|
|
177595
178342
|
}
|
|
177596
178343
|
if (options.buildGraph === true) {
|
|
177597
|
-
scan.graph = buildLinkGraph(codeFiles, docFiles);
|
|
178344
|
+
scan.graph = buildLinkGraph(codeFiles, applied.docFiles);
|
|
177598
178345
|
}
|
|
177599
178346
|
return {
|
|
177600
178347
|
ok: true,
|
|
@@ -177743,7 +178490,7 @@ function resolveLinks(input) {
|
|
|
177743
178490
|
continue;
|
|
177744
178491
|
}
|
|
177745
178492
|
for (const link of file.links) {
|
|
177746
|
-
docToCodePairs.add(
|
|
178493
|
+
docToCodePairs.add(pairKey2(link.source, link.target));
|
|
177747
178494
|
}
|
|
177748
178495
|
}
|
|
177749
178496
|
const codeToDocPairs = new Set;
|
|
@@ -177752,7 +178499,7 @@ function resolveLinks(input) {
|
|
|
177752
178499
|
continue;
|
|
177753
178500
|
}
|
|
177754
178501
|
for (const link of file.links) {
|
|
177755
|
-
codeToDocPairs.add(
|
|
178502
|
+
codeToDocPairs.add(pairKey2(link.source, link.target));
|
|
177756
178503
|
}
|
|
177757
178504
|
}
|
|
177758
178505
|
for (const file of input.codeFiles) {
|
|
@@ -177773,7 +178520,7 @@ function resolveLinks(input) {
|
|
|
177773
178520
|
diagnostics.push(relationshipDiagnostic("doc_anchor_not_found", link.source, docEndpoint, `Doc anchor ${docEndpoint} referenced by ${link.source} does not exist.`, link));
|
|
177774
178521
|
continue;
|
|
177775
178522
|
}
|
|
177776
|
-
if (!docToCodePairs.has(
|
|
178523
|
+
if (!docToCodePairs.has(pairKey2(docEndpoint, link.source))) {
|
|
177777
178524
|
diagnostics.push(relationshipDiagnostic("doc_backlink_not_found", link.source, docEndpoint, `Doc anchor ${docEndpoint} has no matching @code backlink to ${link.source}.`, link));
|
|
177778
178525
|
}
|
|
177779
178526
|
}
|
|
@@ -177792,7 +178539,7 @@ function resolveLinks(input) {
|
|
|
177792
178539
|
diagnostics.push(relationshipDiagnostic("code_file_not_found", link.source, codeEndpoint, `Code file ${codeFilePath} referenced by ${link.source} is not in the managed code set.`, link));
|
|
177793
178540
|
continue;
|
|
177794
178541
|
}
|
|
177795
|
-
if (!codeToDocPairs.has(
|
|
178542
|
+
if (!codeToDocPairs.has(pairKey2(codeEndpoint, link.source))) {
|
|
177796
178543
|
diagnostics.push(relationshipDiagnostic("code_backlink_not_found", link.source, codeEndpoint, `Code endpoint ${codeEndpoint} has no matching @doc pair back to ${link.source}.`, link));
|
|
177797
178544
|
}
|
|
177798
178545
|
}
|
|
@@ -177827,6 +178574,9 @@ function auditUndocumentedSymbols(input, erroredFiles) {
|
|
|
177827
178574
|
continue;
|
|
177828
178575
|
}
|
|
177829
178576
|
for (const symbol of file.undocumentedSymbols) {
|
|
178577
|
+
if (symbol.isMember === true) {
|
|
178578
|
+
continue;
|
|
178579
|
+
}
|
|
177830
178580
|
diagnostics.push({
|
|
177831
178581
|
severity: "warning",
|
|
177832
178582
|
code: "undocumented_symbol",
|
|
@@ -177898,19 +178648,7 @@ function unlinkedDocSectionDiagnostic(node, anchor) {
|
|
|
177898
178648
|
}
|
|
177899
178649
|
return diagnostic;
|
|
177900
178650
|
}
|
|
177901
|
-
function
|
|
177902
|
-
const errored = new Set;
|
|
177903
|
-
for (const diagnostic of diagnostics) {
|
|
177904
|
-
if (diagnostic.code === "file_read_error" || diagnostic.code === "code_parse_error" || isFileScopedScannerDiagnostic(diagnostic)) {
|
|
177905
|
-
errored.add(diagnostic.target);
|
|
177906
|
-
}
|
|
177907
|
-
}
|
|
177908
|
-
return errored;
|
|
177909
|
-
}
|
|
177910
|
-
function isFileScopedScannerDiagnostic(diagnostic) {
|
|
177911
|
-
return (diagnostic.code === "code_scanner_unavailable" || diagnostic.code === "code_scanner_failed") && diagnostic.language !== undefined && diagnostic.target !== diagnostic.language;
|
|
177912
|
-
}
|
|
177913
|
-
function pairKey(source, target) {
|
|
178651
|
+
function pairKey2(source, target) {
|
|
177914
178652
|
return `${source}->${target}`;
|
|
177915
178653
|
}
|
|
177916
178654
|
function relationshipDiagnostic(code, source, target, message, link) {
|
|
@@ -178498,8 +179236,33 @@ function comparePairs(left, right) {
|
|
|
178498
179236
|
return left.docEndpoint.localeCompare(right.docEndpoint) || left.codeEndpoint.localeCompare(right.codeEndpoint);
|
|
178499
179237
|
}
|
|
178500
179238
|
|
|
179239
|
+
// src/core/package-root.ts
|
|
179240
|
+
import { existsSync as existsSync2, realpathSync as realpathSync2 } from "node:fs";
|
|
179241
|
+
import { dirname as dirname3, join as join6 } from "node:path";
|
|
179242
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
179243
|
+
function resolvePackageRoot(moduleUrl = import.meta.url) {
|
|
179244
|
+
const modulePath = fileURLToPath2(moduleUrl);
|
|
179245
|
+
let resolved;
|
|
179246
|
+
try {
|
|
179247
|
+
resolved = realpathSync2(modulePath);
|
|
179248
|
+
} catch {
|
|
179249
|
+
resolved = modulePath;
|
|
179250
|
+
}
|
|
179251
|
+
let dir = dirname3(resolved);
|
|
179252
|
+
for (;; ) {
|
|
179253
|
+
if (existsSync2(join6(dir, "templates", "skills"))) {
|
|
179254
|
+
return dir;
|
|
179255
|
+
}
|
|
179256
|
+
const parent = dirname3(dir);
|
|
179257
|
+
if (parent === dir) {
|
|
179258
|
+
return dir;
|
|
179259
|
+
}
|
|
179260
|
+
dir = parent;
|
|
179261
|
+
}
|
|
179262
|
+
}
|
|
179263
|
+
|
|
178501
179264
|
// src/lsp/server.ts
|
|
178502
|
-
import { fileURLToPath as
|
|
179265
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
178503
179266
|
|
|
178504
179267
|
// src/lsp/position.ts
|
|
178505
179268
|
function toLspPosition(position) {
|
|
@@ -178677,11 +179440,11 @@ function counterpartLocators(state, filePath, position) {
|
|
|
178677
179440
|
|
|
178678
179441
|
// src/lsp/paths.ts
|
|
178679
179442
|
import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2, sep } from "node:path";
|
|
178680
|
-
import { fileURLToPath as
|
|
179443
|
+
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
178681
179444
|
function uriToRelativePath(projectRoot, uri) {
|
|
178682
179445
|
let absolute;
|
|
178683
179446
|
try {
|
|
178684
|
-
absolute =
|
|
179447
|
+
absolute = fileURLToPath3(uri);
|
|
178685
179448
|
} catch {
|
|
178686
179449
|
return;
|
|
178687
179450
|
}
|
|
@@ -179104,7 +179867,7 @@ function runLspServer() {
|
|
|
179104
179867
|
}
|
|
179105
179868
|
function uriToPath(uri) {
|
|
179106
179869
|
try {
|
|
179107
|
-
return
|
|
179870
|
+
return fileURLToPath4(uri);
|
|
179108
179871
|
} catch {
|
|
179109
179872
|
return;
|
|
179110
179873
|
}
|
|
@@ -179138,363 +179901,264 @@ function fullChangeText(changes) {
|
|
|
179138
179901
|
return typeof last.text === "string" ? last.text : undefined;
|
|
179139
179902
|
}
|
|
179140
179903
|
|
|
179141
|
-
// src/
|
|
179142
|
-
import {
|
|
179143
|
-
import {
|
|
179144
|
-
|
|
179145
|
-
|
|
179146
|
-
|
|
179147
|
-
|
|
179148
|
-
|
|
179149
|
-
|
|
179150
|
-
|
|
179151
|
-
|
|
179152
|
-
"
|
|
179153
|
-
"docbridge-
|
|
179154
|
-
"docbridge-review",
|
|
179155
|
-
"docbridge-sync"
|
|
179156
|
-
];
|
|
179157
|
-
var INIT_WITH_AGENT_SKILL_NAMES = ["docbridge-adopt"];
|
|
179158
|
-
function planInitCommand(input) {
|
|
179159
|
-
const packageRoot = input.packageRoot ?? resolvePackageRoot();
|
|
179160
|
-
const existingConfig = readExistingConfig(input.projectRoot);
|
|
179161
|
-
const messages = [];
|
|
179162
|
-
const nextSteps = [];
|
|
179163
|
-
let exitCode = 0;
|
|
179164
|
-
const agentResolution = input.command === "init" ? resolveInitAgentTarget(input.discovery, input.options) : resolveInitWithAgentTarget(input.discovery, input.options);
|
|
179165
|
-
if (agentResolution.error !== undefined) {
|
|
179166
|
-
return {
|
|
179167
|
-
command: input.command,
|
|
179168
|
-
configOps: [],
|
|
179169
|
-
skillOps: [],
|
|
179170
|
-
messages: [agentResolution.error],
|
|
179171
|
-
nextSteps: [],
|
|
179172
|
-
agentGuidance: [],
|
|
179173
|
-
exitCode: 1
|
|
179174
|
-
};
|
|
179175
|
-
}
|
|
179176
|
-
const agentTarget = agentResolution.target ?? "none";
|
|
179177
|
-
const configOps = input.command === "init" ? planConfigOperations({
|
|
179178
|
-
projectRoot: input.projectRoot,
|
|
179179
|
-
options: input.options,
|
|
179180
|
-
discovery: input.discovery,
|
|
179181
|
-
confirmedScope: input.confirmedScope,
|
|
179182
|
-
existingConfig,
|
|
179183
|
-
messages
|
|
179184
|
-
}) : [];
|
|
179185
|
-
if (configOps.some((operation) => operation.action === "create" || operation.action === "would-create")) {
|
|
179186
|
-
nextSteps.push("Review docbridge.config.json and adjust include.docs or include.code if needed.");
|
|
179187
|
-
nextSteps.push("Add @doc and @code annotations to link code and documentation.");
|
|
179188
|
-
nextSteps.push("Run docbridge check when links exist.");
|
|
179189
|
-
} else if (existingConfig.ok) {
|
|
179190
|
-
messages.push("Existing docbridge.config.json was left unchanged.");
|
|
179191
|
-
summarizeExistingConfig(existingConfig.config, messages);
|
|
179192
|
-
} else if (existingConfig.rawText !== undefined) {
|
|
179193
|
-
messages.push("Existing docbridge.config.json is invalid and was not modified.");
|
|
179194
|
-
for (const diagnostic of existingConfig.diagnostics) {
|
|
179195
|
-
messages.push(diagnostic.message);
|
|
179196
|
-
}
|
|
179197
|
-
messages.push("Repair the config manually or delete it before re-running init.");
|
|
179198
|
-
exitCode = 1;
|
|
179199
|
-
}
|
|
179200
|
-
const skillOps = planSkillOperations({
|
|
179201
|
-
command: input.command,
|
|
179202
|
-
projectRoot: input.projectRoot,
|
|
179203
|
-
packageRoot,
|
|
179204
|
-
agentTarget,
|
|
179205
|
-
options: input.options
|
|
179206
|
-
});
|
|
179207
|
-
const agentGuidance = input.command === "init-with-agent" && agentTarget !== "none" ? buildAgentGuidance(input.projectRoot, agentTarget) : [];
|
|
179208
|
-
if (input.command === "init-with-agent") {
|
|
179209
|
-
nextSteps.push("Run the printed one-shot command or fallback prompt in your agent.");
|
|
179210
|
-
nextSteps.push("Let docbridge-adopt confirm scope and create or improve docbridge.config.json.");
|
|
179211
|
-
}
|
|
179212
|
-
if (input.options.yes && input.command === "init" && configOps.length === 0 && existingConfig.rawText === undefined && (input.discovery.docs.ambiguous || input.discovery.code.ambiguous)) {
|
|
179213
|
-
exitCode = 1;
|
|
179214
|
-
}
|
|
179215
|
-
return {
|
|
179216
|
-
command: input.command,
|
|
179217
|
-
configOps,
|
|
179218
|
-
skillOps,
|
|
179219
|
-
messages,
|
|
179220
|
-
nextSteps,
|
|
179221
|
-
agentGuidance,
|
|
179222
|
-
exitCode
|
|
179223
|
-
};
|
|
179224
|
-
}
|
|
179225
|
-
function buildConfigFromScope(scope) {
|
|
179226
|
-
const code = {};
|
|
179227
|
-
for (const language of scope.languages) {
|
|
179228
|
-
code[language.language] = { patterns: [...language.patterns] };
|
|
179229
|
-
}
|
|
179230
|
-
return {
|
|
179231
|
-
include: {
|
|
179232
|
-
code,
|
|
179233
|
-
docs: [scope.docsPattern]
|
|
179234
|
-
}
|
|
179235
|
-
};
|
|
179904
|
+
// src/setup/update-cache.ts
|
|
179905
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync } from "node:fs";
|
|
179906
|
+
import { homedir } from "node:os";
|
|
179907
|
+
import { dirname as dirname4, join as join7 } from "node:path";
|
|
179908
|
+
var UPDATE_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
179909
|
+
var UPDATE_CACHE_SCHEMA_VERSION = 1;
|
|
179910
|
+
function resolveUpdateCachePath(env = process.env, home = homedir()) {
|
|
179911
|
+
const explicit = env.DOCBRIDGE_UPDATE_CACHE;
|
|
179912
|
+
if (explicit !== undefined && explicit.length > 0) {
|
|
179913
|
+
return explicit;
|
|
179914
|
+
}
|
|
179915
|
+
const cacheRoot = env.XDG_CACHE_HOME !== undefined && env.XDG_CACHE_HOME.length > 0 ? env.XDG_CACHE_HOME : join7(home, ".cache");
|
|
179916
|
+
return join7(cacheRoot, "docbridge", "update-check.json");
|
|
179236
179917
|
}
|
|
179237
|
-
function
|
|
179238
|
-
|
|
179239
|
-
let resolved;
|
|
179918
|
+
function readUpdateCache(path) {
|
|
179919
|
+
let rawText;
|
|
179240
179920
|
try {
|
|
179241
|
-
|
|
179921
|
+
rawText = readFileSync4(path, "utf8");
|
|
179242
179922
|
} catch {
|
|
179243
|
-
|
|
179244
|
-
}
|
|
179245
|
-
let dir = dirname3(resolved);
|
|
179246
|
-
for (;; ) {
|
|
179247
|
-
if (existsSync2(join5(dir, "templates", "skills"))) {
|
|
179248
|
-
return dir;
|
|
179249
|
-
}
|
|
179250
|
-
const parent = dirname3(dir);
|
|
179251
|
-
if (parent === dir) {
|
|
179252
|
-
return dir;
|
|
179253
|
-
}
|
|
179254
|
-
dir = parent;
|
|
179923
|
+
return;
|
|
179255
179924
|
}
|
|
179256
|
-
|
|
179257
|
-
function listDistributableSkills(packageRoot) {
|
|
179258
|
-
const skillsRoot = join5(packageRoot, "templates", "skills");
|
|
179259
|
-
let entries;
|
|
179925
|
+
let parsed;
|
|
179260
179926
|
try {
|
|
179261
|
-
|
|
179927
|
+
parsed = JSON.parse(rawText);
|
|
179262
179928
|
} catch {
|
|
179263
|
-
return
|
|
179929
|
+
return;
|
|
179264
179930
|
}
|
|
179265
|
-
return
|
|
179931
|
+
return toUpdateCacheRecord(parsed);
|
|
179266
179932
|
}
|
|
179267
|
-
function
|
|
179268
|
-
|
|
179269
|
-
|
|
179270
|
-
|
|
179271
|
-
|
|
179272
|
-
|
|
179273
|
-
lines.push("");
|
|
179274
|
-
lines.push("Config:");
|
|
179275
|
-
for (const operation of plan.configOps) {
|
|
179276
|
-
lines.push(formatFileOp(operation));
|
|
179277
|
-
if (operation.content !== undefined) {
|
|
179278
|
-
lines.push(operation.content);
|
|
179279
|
-
}
|
|
179280
|
-
}
|
|
179281
|
-
}
|
|
179282
|
-
if (plan.skillOps.length > 0) {
|
|
179283
|
-
lines.push("");
|
|
179284
|
-
lines.push("Skills:");
|
|
179285
|
-
for (const operation of plan.skillOps) {
|
|
179286
|
-
lines.push(formatFileOp(operation));
|
|
179287
|
-
}
|
|
179288
|
-
}
|
|
179289
|
-
if (plan.agentGuidance.length > 0) {
|
|
179290
|
-
lines.push("");
|
|
179291
|
-
lines.push("Agent setup:");
|
|
179292
|
-
for (const guidance of plan.agentGuidance) {
|
|
179293
|
-
lines.push("");
|
|
179294
|
-
lines.push(`${capitalize(guidance.agent)} destination: ${guidance.destination}`);
|
|
179295
|
-
lines.push(`One-shot command: ${guidance.oneShotCommand}`);
|
|
179296
|
-
lines.push("Fallback prompt:");
|
|
179297
|
-
lines.push(guidance.fallbackPrompt);
|
|
179298
|
-
}
|
|
179299
|
-
}
|
|
179300
|
-
if (plan.nextSteps.length > 0) {
|
|
179301
|
-
lines.push("");
|
|
179302
|
-
lines.push("Next steps:");
|
|
179303
|
-
for (const step of plan.nextSteps) {
|
|
179304
|
-
lines.push(`- ${step}`);
|
|
179305
|
-
}
|
|
179306
|
-
}
|
|
179307
|
-
return `${lines.join(`
|
|
179308
|
-
`)}
|
|
179309
|
-
`;
|
|
179933
|
+
function writeUpdateCache(path, record) {
|
|
179934
|
+
try {
|
|
179935
|
+
mkdirSync2(dirname4(path), { recursive: true });
|
|
179936
|
+
writeFileSync(path, `${JSON.stringify(record, null, 2)}
|
|
179937
|
+
`, "utf8");
|
|
179938
|
+
} catch {}
|
|
179310
179939
|
}
|
|
179311
|
-
function
|
|
179312
|
-
|
|
179313
|
-
|
|
179314
|
-
}
|
|
179315
|
-
const scope = input.confirmedScope ?? inferConfirmedScope(input.discovery, input.options);
|
|
179316
|
-
if (scope === undefined) {
|
|
179317
|
-
if (input.discovery.docs.message !== undefined) {
|
|
179318
|
-
input.messages.push(input.discovery.docs.message);
|
|
179319
|
-
}
|
|
179320
|
-
if (input.discovery.code.message !== undefined) {
|
|
179321
|
-
input.messages.push(input.discovery.code.message);
|
|
179322
|
-
}
|
|
179323
|
-
if (input.options.yes) {
|
|
179324
|
-
input.messages.push("Automatic setup stopped because docs or code scope is ambiguous. Re-run interactively or confirm scope explicitly.");
|
|
179325
|
-
}
|
|
179326
|
-
return [];
|
|
179327
|
-
}
|
|
179328
|
-
const config = buildConfigFromScope(scope);
|
|
179329
|
-
const content = `${JSON.stringify({ $schema: "./schemas/docbridge.schema.json", ...config }, null, 2)}
|
|
179330
|
-
`;
|
|
179331
|
-
const action = input.options.dryRun ? "would-create" : "create";
|
|
179332
|
-
input.messages.push(`Docs scope: ${scope.docsPattern}`);
|
|
179333
|
-
input.messages.push(`Code scope: ${scope.languages.map((language) => `${language.language} (${language.patterns.join(", ")})`).join("; ")}`);
|
|
179334
|
-
return [
|
|
179335
|
-
{
|
|
179336
|
-
action,
|
|
179337
|
-
path: CONFIG_FILE_NAME2,
|
|
179338
|
-
content,
|
|
179339
|
-
reason: "Create docbridge.config.json from discovered scope."
|
|
179340
|
-
}
|
|
179341
|
-
];
|
|
179940
|
+
function isUpdateCacheFresh(record, now, ttlMs = UPDATE_CACHE_TTL_MS) {
|
|
179941
|
+
const age = now - record.checkedAt;
|
|
179942
|
+
return age >= 0 && age < ttlMs;
|
|
179342
179943
|
}
|
|
179343
|
-
function
|
|
179344
|
-
if (
|
|
179944
|
+
function toUpdateCacheRecord(value) {
|
|
179945
|
+
if (typeof value !== "object" || value === null) {
|
|
179345
179946
|
return;
|
|
179346
179947
|
}
|
|
179347
|
-
|
|
179948
|
+
const candidate = value;
|
|
179949
|
+
if (candidate.schema !== UPDATE_CACHE_SCHEMA_VERSION) {
|
|
179348
179950
|
return;
|
|
179349
179951
|
}
|
|
179350
|
-
if (!
|
|
179952
|
+
if (typeof candidate.checkedAt !== "number" || !Number.isFinite(candidate.checkedAt)) {
|
|
179953
|
+
return;
|
|
179954
|
+
}
|
|
179955
|
+
if (candidate.latest !== null && typeof candidate.latest !== "string") {
|
|
179351
179956
|
return;
|
|
179352
179957
|
}
|
|
179353
179958
|
return {
|
|
179354
|
-
|
|
179355
|
-
|
|
179959
|
+
schema: UPDATE_CACHE_SCHEMA_VERSION,
|
|
179960
|
+
checkedAt: candidate.checkedAt,
|
|
179961
|
+
latest: candidate.latest
|
|
179356
179962
|
};
|
|
179357
179963
|
}
|
|
179358
|
-
|
|
179359
|
-
|
|
179360
|
-
|
|
179964
|
+
|
|
179965
|
+
// src/setup/version.ts
|
|
179966
|
+
var STABLE_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
|
|
179967
|
+
function parseStableVersion(value) {
|
|
179968
|
+
const match = STABLE_VERSION_PATTERN.exec(value.trim());
|
|
179969
|
+
if (match === null) {
|
|
179970
|
+
return;
|
|
179361
179971
|
}
|
|
179362
|
-
const
|
|
179363
|
-
|
|
179364
|
-
|
|
179365
|
-
const operations = [];
|
|
179366
|
-
for (const destination of destinations) {
|
|
179367
|
-
for (const skillName of skillNames) {
|
|
179368
|
-
if (!availableSkills.has(skillName)) {
|
|
179369
|
-
continue;
|
|
179370
|
-
}
|
|
179371
|
-
const destinationDir = join5(input.projectRoot, destination, skillName);
|
|
179372
|
-
const exists = existsSync2(destinationDir);
|
|
179373
|
-
let action;
|
|
179374
|
-
let reason;
|
|
179375
|
-
if (exists && input.options.force) {
|
|
179376
|
-
action = input.options.dryRun ? "would-overwrite" : "overwrite";
|
|
179377
|
-
reason = "Overwrite existing skill directory.";
|
|
179378
|
-
} else if (exists) {
|
|
179379
|
-
action = "skip";
|
|
179380
|
-
reason = "Skill directory already exists.";
|
|
179381
|
-
} else {
|
|
179382
|
-
action = input.options.dryRun ? "would-create" : "create";
|
|
179383
|
-
reason = "Install distributable skill template.";
|
|
179384
|
-
}
|
|
179385
|
-
operations.push({
|
|
179386
|
-
action,
|
|
179387
|
-
path: relative3(input.projectRoot, destinationDir),
|
|
179388
|
-
reason
|
|
179389
|
-
});
|
|
179390
|
-
}
|
|
179972
|
+
const [, major, minor, patch] = match;
|
|
179973
|
+
if (major === undefined || minor === undefined || patch === undefined) {
|
|
179974
|
+
return;
|
|
179391
179975
|
}
|
|
179392
|
-
return
|
|
179976
|
+
return {
|
|
179977
|
+
major: Number.parseInt(major, 10),
|
|
179978
|
+
minor: Number.parseInt(minor, 10),
|
|
179979
|
+
patch: Number.parseInt(patch, 10)
|
|
179980
|
+
};
|
|
179393
179981
|
}
|
|
179394
|
-
function
|
|
179395
|
-
|
|
179396
|
-
if (agentTarget === "codex" || agentTarget === "both") {
|
|
179397
|
-
guidance.push({
|
|
179398
|
-
agent: "codex",
|
|
179399
|
-
destination: ".agents/skills/docbridge-adopt/",
|
|
179400
|
-
oneShotCommand: `cd ${projectRoot} && $docbridge_adopt_prompt`,
|
|
179401
|
-
fallbackPrompt: buildFallbackPrompt(projectRoot, "codex")
|
|
179402
|
-
});
|
|
179403
|
-
}
|
|
179404
|
-
if (agentTarget === "claude" || agentTarget === "both") {
|
|
179405
|
-
guidance.push({
|
|
179406
|
-
agent: "claude",
|
|
179407
|
-
destination: ".claude/skills/docbridge-adopt/",
|
|
179408
|
-
oneShotCommand: `cd ${projectRoot} && /docbridge-adopt`,
|
|
179409
|
-
fallbackPrompt: buildFallbackPrompt(projectRoot, "claude")
|
|
179410
|
-
});
|
|
179411
|
-
}
|
|
179412
|
-
for (const entry of guidance) {
|
|
179413
|
-
entry.oneShotCommand = entry.oneShotCommand.replace("$docbridge_adopt_prompt", 'codex --prompt "Use the docbridge-adopt skill to adopt DocBridge in this repository."');
|
|
179414
|
-
}
|
|
179415
|
-
return guidance;
|
|
179982
|
+
function compareStableVersions(left, right) {
|
|
179983
|
+
return left.major - right.major || left.minor - right.minor || left.patch - right.patch;
|
|
179416
179984
|
}
|
|
179417
|
-
function
|
|
179418
|
-
const
|
|
179419
|
-
|
|
179985
|
+
function isNewerStableVersion(candidate, current) {
|
|
179986
|
+
const parsedCandidate = parseStableVersion(candidate);
|
|
179987
|
+
const parsedCurrent = parseStableVersion(current);
|
|
179988
|
+
if (parsedCandidate === undefined || parsedCurrent === undefined) {
|
|
179989
|
+
return false;
|
|
179990
|
+
}
|
|
179991
|
+
return compareStableVersions(parsedCandidate, parsedCurrent) > 0;
|
|
179420
179992
|
}
|
|
179421
|
-
|
|
179422
|
-
|
|
179423
|
-
|
|
179993
|
+
|
|
179994
|
+
// src/setup/registry.ts
|
|
179995
|
+
var NPM_REGISTRY_LATEST_URL = "https://registry.npmjs.org/docbridge/latest";
|
|
179996
|
+
var REGISTRY_TIMEOUT_MS = 1500;
|
|
179997
|
+
function selectStableVersion(payload) {
|
|
179998
|
+
if (typeof payload !== "object" || payload === null) {
|
|
179999
|
+
return;
|
|
179424
180000
|
}
|
|
179425
|
-
|
|
179426
|
-
|
|
180001
|
+
const version = payload.version;
|
|
180002
|
+
if (typeof version !== "string") {
|
|
180003
|
+
return;
|
|
179427
180004
|
}
|
|
179428
|
-
return
|
|
180005
|
+
return parseStableVersion(version) === undefined ? undefined : version.trim();
|
|
179429
180006
|
}
|
|
179430
|
-
function
|
|
179431
|
-
|
|
179432
|
-
|
|
179433
|
-
|
|
179434
|
-
|
|
179435
|
-
|
|
179436
|
-
|
|
180007
|
+
function resolveRegistryUrl(env = process.env) {
|
|
180008
|
+
const override = env.DOCBRIDGE_REGISTRY_URL;
|
|
180009
|
+
return override !== undefined && override.length > 0 ? override : NPM_REGISTRY_LATEST_URL;
|
|
180010
|
+
}
|
|
180011
|
+
async function fetchLatestStableVersion(options = {}) {
|
|
180012
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
180013
|
+
const url = options.url ?? resolveRegistryUrl();
|
|
180014
|
+
try {
|
|
180015
|
+
const response = await fetchImpl(url, {
|
|
180016
|
+
signal: AbortSignal.timeout(options.timeoutMs ?? REGISTRY_TIMEOUT_MS),
|
|
180017
|
+
headers: { accept: "application/json" }
|
|
180018
|
+
});
|
|
180019
|
+
if (!response.ok) {
|
|
180020
|
+
return;
|
|
179437
180021
|
}
|
|
179438
|
-
return
|
|
180022
|
+
return selectStableVersion(await response.json());
|
|
180023
|
+
} catch {
|
|
180024
|
+
return;
|
|
179439
180025
|
}
|
|
179440
|
-
|
|
179441
|
-
|
|
179442
|
-
|
|
179443
|
-
|
|
179444
|
-
|
|
179445
|
-
|
|
179446
|
-
|
|
179447
|
-
|
|
180026
|
+
}
|
|
180027
|
+
async function resolveLatestStableVersion(options = {}) {
|
|
180028
|
+
const now = options.now ?? Date.now();
|
|
180029
|
+
const cachePath = options.cachePath ?? resolveUpdateCachePath();
|
|
180030
|
+
if (options.forceRefresh !== true) {
|
|
180031
|
+
const cached = readUpdateCache(cachePath);
|
|
180032
|
+
if (cached !== undefined && isUpdateCacheFresh(cached, now)) {
|
|
180033
|
+
return cached.latest === null ? { status: "unavailable", source: "cache" } : { status: "ok", latest: cached.latest, source: "cache" };
|
|
180034
|
+
}
|
|
180035
|
+
}
|
|
180036
|
+
const fetched = await fetchLatestStableVersion({
|
|
180037
|
+
...options.url !== undefined ? { url: options.url } : {},
|
|
180038
|
+
...options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {},
|
|
180039
|
+
...options.fetchImpl !== undefined ? { fetchImpl: options.fetchImpl } : {}
|
|
180040
|
+
});
|
|
180041
|
+
writeUpdateCache(cachePath, {
|
|
180042
|
+
schema: UPDATE_CACHE_SCHEMA_VERSION,
|
|
180043
|
+
checkedAt: now,
|
|
180044
|
+
latest: fetched ?? null
|
|
180045
|
+
});
|
|
180046
|
+
return fetched === undefined ? { status: "unavailable", source: "network" } : { status: "ok", latest: fetched, source: "network" };
|
|
180047
|
+
}
|
|
180048
|
+
|
|
180049
|
+
// src/setup/upgrade-guidance.ts
|
|
180050
|
+
import { sep as sep2 } from "node:path";
|
|
180051
|
+
var PACKAGE_MANAGERS = ["bun", "npm", "pnpm", "yarn"];
|
|
180052
|
+
var PROJECT_COMMANDS = {
|
|
180053
|
+
bun: "bun add -d docbridge@latest",
|
|
180054
|
+
npm: "npm install --save-dev docbridge@latest",
|
|
180055
|
+
pnpm: "pnpm add -D docbridge@latest",
|
|
180056
|
+
yarn: "yarn add -D docbridge@latest"
|
|
180057
|
+
};
|
|
180058
|
+
var GLOBAL_COMMANDS = {
|
|
180059
|
+
bun: "bun add -g docbridge@latest",
|
|
180060
|
+
npm: "npm install -g docbridge@latest",
|
|
180061
|
+
pnpm: "pnpm add -g docbridge@latest",
|
|
180062
|
+
yarn: "yarn global add docbridge@latest"
|
|
180063
|
+
};
|
|
180064
|
+
function detectPackageManager(env = process.env) {
|
|
180065
|
+
const userAgent = env.npm_config_user_agent;
|
|
180066
|
+
if (userAgent === undefined || userAgent.length === 0) {
|
|
180067
|
+
return;
|
|
179448
180068
|
}
|
|
179449
|
-
|
|
180069
|
+
const name = userAgent.trim().split("/", 1)[0]?.toLowerCase();
|
|
180070
|
+
return PACKAGE_MANAGERS.find((manager) => manager === name);
|
|
179450
180071
|
}
|
|
179451
|
-
function
|
|
179452
|
-
const
|
|
179453
|
-
|
|
179454
|
-
|
|
179455
|
-
rawText = readFileSync3(configPath, "utf8");
|
|
179456
|
-
} catch {
|
|
179457
|
-
rawText = undefined;
|
|
180072
|
+
function detectInstallScope(packageRoot, candidateRoots) {
|
|
180073
|
+
const installBase = resolveInstallBase(packageRoot);
|
|
180074
|
+
if (installBase === undefined) {
|
|
180075
|
+
return "unknown";
|
|
179458
180076
|
}
|
|
179459
|
-
|
|
180077
|
+
return candidateRoots.some((root) => isSameOrAncestor(installBase, root)) ? "project" : "global";
|
|
180078
|
+
}
|
|
180079
|
+
function detectUpgradeGuidance(input) {
|
|
180080
|
+
const packageManager = detectPackageManager(input.env ?? process.env);
|
|
180081
|
+
const scope = detectInstallScope(input.packageRoot, [
|
|
180082
|
+
input.projectRoot,
|
|
180083
|
+
input.currentDirectory ?? process.cwd()
|
|
180084
|
+
]);
|
|
180085
|
+
const commands = scope === "global" ? GLOBAL_COMMANDS : PROJECT_COMMANDS;
|
|
180086
|
+
const primaryManager = packageManager ?? "npm";
|
|
179460
180087
|
return {
|
|
179461
|
-
|
|
179462
|
-
|
|
179463
|
-
|
|
179464
|
-
|
|
180088
|
+
packageManager,
|
|
180089
|
+
scope,
|
|
180090
|
+
primaryCommand: commands[primaryManager],
|
|
180091
|
+
alternativeCommands: PACKAGE_MANAGERS.filter((manager) => manager !== primaryManager).map((manager) => commands[manager])
|
|
179465
180092
|
};
|
|
179466
180093
|
}
|
|
179467
|
-
function
|
|
179468
|
-
|
|
179469
|
-
|
|
179470
|
-
|
|
180094
|
+
function formatUpgradeGuidance(guidance) {
|
|
180095
|
+
const scopeLabel = guidance.scope === "unknown" ? "install scope not detected" : `${guidance.scope} install`;
|
|
180096
|
+
return [
|
|
180097
|
+
`Upgrade command (${scopeLabel}): ${guidance.primaryCommand}`,
|
|
180098
|
+
`Other package managers: ${guidance.alternativeCommands.join(", ")}`
|
|
180099
|
+
];
|
|
179471
180100
|
}
|
|
179472
|
-
function
|
|
179473
|
-
|
|
179474
|
-
|
|
179475
|
-
|
|
179476
|
-
|
|
179477
|
-
|
|
179478
|
-
|
|
179479
|
-
|
|
179480
|
-
|
|
179481
|
-
|
|
180101
|
+
function resolveInstallBase(packageRoot) {
|
|
180102
|
+
const marker = `${sep2}node_modules${sep2}`;
|
|
180103
|
+
const markerIndex = withTrailingSeparator(packageRoot).indexOf(marker);
|
|
180104
|
+
return markerIndex === -1 ? undefined : packageRoot.slice(0, markerIndex);
|
|
180105
|
+
}
|
|
180106
|
+
function isSameOrAncestor(candidate, descendant) {
|
|
180107
|
+
return withTrailingSeparator(descendant).startsWith(withTrailingSeparator(candidate));
|
|
180108
|
+
}
|
|
180109
|
+
function withTrailingSeparator(path) {
|
|
180110
|
+
return path.endsWith(sep2) ? path : `${path}${sep2}`;
|
|
180111
|
+
}
|
|
180112
|
+
|
|
180113
|
+
// src/setup/update-notice.ts
|
|
180114
|
+
var SUPPRESSED_COMMANDS = new Set(["lsp", "upgrade"]);
|
|
180115
|
+
function isUpdateCheckOptedOut(env = process.env) {
|
|
180116
|
+
return isEnabledFlag(env.DOCBRIDGE_NO_UPDATE_CHECK);
|
|
180117
|
+
}
|
|
180118
|
+
function decideUpdateCheck(input) {
|
|
180119
|
+
const env = input.env ?? process.env;
|
|
180120
|
+
if (isUpdateCheckOptedOut(env)) {
|
|
180121
|
+
return { enabled: false, reason: "opt-out" };
|
|
180122
|
+
}
|
|
180123
|
+
if (isEnabledFlag(env.CI)) {
|
|
180124
|
+
return { enabled: false, reason: "ci" };
|
|
179482
180125
|
}
|
|
180126
|
+
if (!input.isTty) {
|
|
180127
|
+
return { enabled: false, reason: "non-tty" };
|
|
180128
|
+
}
|
|
180129
|
+
if (input.argv.includes("--json")) {
|
|
180130
|
+
return { enabled: false, reason: "json-output" };
|
|
180131
|
+
}
|
|
180132
|
+
const command = input.argv[0];
|
|
180133
|
+
if (command !== undefined && SUPPRESSED_COMMANDS.has(command)) {
|
|
180134
|
+
return { enabled: false, reason: "machine-command" };
|
|
180135
|
+
}
|
|
180136
|
+
return { enabled: true };
|
|
179483
180137
|
}
|
|
179484
|
-
function
|
|
179485
|
-
|
|
179486
|
-
|
|
179487
|
-
label = "would create";
|
|
179488
|
-
} else if (operation.action === "would-overwrite") {
|
|
179489
|
-
label = "would overwrite";
|
|
180138
|
+
function formatUpdateNotice(input) {
|
|
180139
|
+
if (!isNewerStableVersion(input.latest, input.current)) {
|
|
180140
|
+
return;
|
|
179490
180141
|
}
|
|
179491
|
-
|
|
179492
|
-
|
|
180142
|
+
return [
|
|
180143
|
+
`Update available: docbridge ${input.current} -> ${input.latest}`,
|
|
180144
|
+
formatUpgradeGuidance(input.guidance)[0] ?? "",
|
|
180145
|
+
"Run `docbridge upgrade --check` for details, or set DOCBRIDGE_NO_UPDATE_CHECK=1 to silence this notice.",
|
|
180146
|
+
""
|
|
180147
|
+
].join(`
|
|
180148
|
+
`);
|
|
179493
180149
|
}
|
|
179494
|
-
function
|
|
179495
|
-
|
|
180150
|
+
function isEnabledFlag(value) {
|
|
180151
|
+
if (value === undefined) {
|
|
180152
|
+
return false;
|
|
180153
|
+
}
|
|
180154
|
+
const normalized = value.trim().toLowerCase();
|
|
180155
|
+
return normalized.length > 0 && normalized !== "0" && normalized !== "false";
|
|
179496
180156
|
}
|
|
179497
180157
|
|
|
180158
|
+
// src/cli/docs.ts
|
|
180159
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync5 } from "node:fs";
|
|
180160
|
+
import { join as join8 } from "node:path";
|
|
180161
|
+
|
|
179498
180162
|
// src/cli/errors.ts
|
|
179499
180163
|
class CliError extends Error {
|
|
179500
180164
|
guidance;
|
|
@@ -179555,6 +180219,19 @@ function agentTargetGuidance(command) {
|
|
|
179555
180219
|
].join(`
|
|
179556
180220
|
`);
|
|
179557
180221
|
}
|
|
180222
|
+
function upgradeConfirmationGuidance() {
|
|
180223
|
+
return [
|
|
180224
|
+
"Confirm the migration explicitly:",
|
|
180225
|
+
"",
|
|
180226
|
+
" docbridge upgrade --force --yes",
|
|
180227
|
+
"",
|
|
180228
|
+
"Or inspect it first:",
|
|
180229
|
+
"",
|
|
180230
|
+
" docbridge upgrade --check",
|
|
180231
|
+
" docbridge upgrade --force --dry-run"
|
|
180232
|
+
].join(`
|
|
180233
|
+
`);
|
|
180234
|
+
}
|
|
179558
180235
|
function includeContentGuidance() {
|
|
179559
180236
|
return [
|
|
179560
180237
|
"Use --include-content together with --json:",
|
|
@@ -179581,6 +180258,9 @@ function configSetupGuidance() {
|
|
|
179581
180258
|
function configRepairGuidance() {
|
|
179582
180259
|
return "Repair or delete docbridge.config.json, then re-run `docbridge check`.";
|
|
179583
180260
|
}
|
|
180261
|
+
function manifestRepairGuidance() {
|
|
180262
|
+
return "Repair or delete docbridge.links.json, then re-run `docbridge check`.";
|
|
180263
|
+
}
|
|
179584
180264
|
function formatCliError(error) {
|
|
179585
180265
|
if (!(error instanceof CliError)) {
|
|
179586
180266
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -179648,11 +180328,11 @@ function parseDocument(name, source) {
|
|
|
179648
180328
|
};
|
|
179649
180329
|
}
|
|
179650
180330
|
function createFileDocumentationReader(packageRoot) {
|
|
179651
|
-
const docsRoot =
|
|
180331
|
+
const docsRoot = join8(packageRoot, "docs", "user");
|
|
179652
180332
|
function documents() {
|
|
179653
180333
|
let fileNames;
|
|
179654
180334
|
try {
|
|
179655
|
-
fileNames =
|
|
180335
|
+
fileNames = readdirSync2(docsRoot).filter((fileName) => fileName.endsWith(".md")).toSorted();
|
|
179656
180336
|
} catch {
|
|
179657
180337
|
throw documentationUnavailableError();
|
|
179658
180338
|
}
|
|
@@ -179661,7 +180341,7 @@ function createFileDocumentationReader(packageRoot) {
|
|
|
179661
180341
|
}
|
|
179662
180342
|
return fileNames.map((fileName) => {
|
|
179663
180343
|
const name = fileName.slice(0, -".md".length);
|
|
179664
|
-
return parseDocument(name,
|
|
180344
|
+
return parseDocument(name, readFileSync5(join8(docsRoot, fileName), "utf8"));
|
|
179665
180345
|
});
|
|
179666
180346
|
}
|
|
179667
180347
|
return {
|
|
@@ -179732,6 +180412,7 @@ var SUBCOMMANDS = [
|
|
|
179732
180412
|
"docs",
|
|
179733
180413
|
"init",
|
|
179734
180414
|
"init-with-agent",
|
|
180415
|
+
"upgrade",
|
|
179735
180416
|
"lsp"
|
|
179736
180417
|
];
|
|
179737
180418
|
var ROOT_OPTION = {
|
|
@@ -179857,11 +180538,47 @@ var COMMAND_DOCS = {
|
|
|
179857
180538
|
`),
|
|
179858
180539
|
options: [JSON_OPTION]
|
|
179859
180540
|
},
|
|
180541
|
+
upgrade: {
|
|
180542
|
+
usage: "[options]",
|
|
180543
|
+
summary: "Use to check for a newer release and migrate managed skills.",
|
|
180544
|
+
description: [
|
|
180545
|
+
"Report DocBridge version drift and reconcile the managed agent skill.",
|
|
180546
|
+
"Use it after a DocBridge release, or when a project still carries assets",
|
|
180547
|
+
"from an older layout. `--check` is read-only. Applying changes touches",
|
|
180548
|
+
"only the managed docbridge skill directory and the known legacy skill",
|
|
180549
|
+
"directories; it never edits config, hooks, CI recipes, or user code, and",
|
|
180550
|
+
"it never runs a package manager for you."
|
|
180551
|
+
].join(`
|
|
180552
|
+
`),
|
|
180553
|
+
options: [
|
|
180554
|
+
{
|
|
180555
|
+
flag: "--root <path>",
|
|
180556
|
+
description: "Project root to inspect. Defaults to current directory."
|
|
180557
|
+
},
|
|
180558
|
+
{
|
|
180559
|
+
flag: "--check",
|
|
180560
|
+
description: "Report version and managed asset state without writing files."
|
|
180561
|
+
},
|
|
180562
|
+
{
|
|
180563
|
+
flag: "--dry-run",
|
|
180564
|
+
description: "Print the operations that would be applied without writing files."
|
|
180565
|
+
},
|
|
180566
|
+
{ flag: "--yes", description: "Confirm destructive operations without prompting." },
|
|
180567
|
+
{
|
|
180568
|
+
flag: "--force",
|
|
180569
|
+
description: "Replace the managed skill and remove known legacy skill directories."
|
|
180570
|
+
},
|
|
180571
|
+
{
|
|
180572
|
+
flag: "--agent-target <target>",
|
|
180573
|
+
description: "Agent target: codex, claude, both, or none."
|
|
180574
|
+
}
|
|
180575
|
+
]
|
|
180576
|
+
},
|
|
179860
180577
|
init: {
|
|
179861
180578
|
usage: "[options]",
|
|
179862
|
-
summary: "Use once per project to set up config and agent
|
|
180579
|
+
summary: "Use once per project to set up config and the agent skill.",
|
|
179863
180580
|
description: [
|
|
179864
|
-
"Create docbridge.config.json and install the DocBridge agent
|
|
180581
|
+
"Create docbridge.config.json and install the DocBridge agent skill.",
|
|
179865
180582
|
"Use it once per project, when adopting DocBridge. Run it with --dry-run",
|
|
179866
180583
|
"first to see what would be written."
|
|
179867
180584
|
].join(`
|
|
@@ -179878,7 +180595,7 @@ var COMMAND_DOCS = {
|
|
|
179878
180595
|
usage: "[options]",
|
|
179879
180596
|
summary: "Use to let a coding agent decide the setup instead of `init`.",
|
|
179880
180597
|
description: [
|
|
179881
|
-
"Install the docbridge
|
|
180598
|
+
"Install the docbridge skill and print the commands that let a coding",
|
|
179882
180599
|
"agent finish the setup.",
|
|
179883
180600
|
"Use it instead of `init` when an agent should decide the configuration",
|
|
179884
180601
|
"rather than accepting the defaults."
|
|
@@ -179924,7 +180641,7 @@ function commandList() {
|
|
|
179924
180641
|
return SUBCOMMANDS.map((command) => ` ${command.padEnd(width)}${COMMAND_DOCS[command].summary}`).join(`
|
|
179925
180642
|
`);
|
|
179926
180643
|
}
|
|
179927
|
-
var GLOBAL_OPTION_SECTIONS = ["check", "related", "context", "graph", "docs", "init"].map((command) => {
|
|
180644
|
+
var GLOBAL_OPTION_SECTIONS = ["check", "related", "context", "graph", "docs", "init", "upgrade"].map((command) => {
|
|
179928
180645
|
const label = `${command.charAt(0).toUpperCase()}${command.slice(1)}`;
|
|
179929
180646
|
return `${label} options:
|
|
179930
180647
|
${renderOptions(COMMAND_DOCS[command].options)}`;
|
|
@@ -180021,12 +180738,12 @@ function hasHelpFlag(args) {
|
|
|
180021
180738
|
}
|
|
180022
180739
|
|
|
180023
180740
|
// src/cli/init.ts
|
|
180024
|
-
import {
|
|
180025
|
-
import { join as
|
|
180741
|
+
import { readFileSync as readFileSync8, statSync as statSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
180742
|
+
import { join as join12, resolve as resolve3 } from "node:path";
|
|
180026
180743
|
|
|
180027
|
-
// src/
|
|
180028
|
-
import { existsSync as existsSync3, readdirSync as
|
|
180029
|
-
import { basename, dirname as
|
|
180744
|
+
// src/setup/init-discovery.ts
|
|
180745
|
+
import { existsSync as existsSync3, readdirSync as readdirSync3, statSync as statSync2 } from "node:fs";
|
|
180746
|
+
import { basename, dirname as dirname5, join as join9 } from "node:path";
|
|
180030
180747
|
var HIGH_CONFIDENCE_DIR_NAMES = new Set([
|
|
180031
180748
|
"docs/specs",
|
|
180032
180749
|
"specs",
|
|
@@ -180091,7 +180808,7 @@ function discoverDocsScope(projectRoot) {
|
|
|
180091
180808
|
}
|
|
180092
180809
|
const grouped = new Map;
|
|
180093
180810
|
for (const filePath of eligible) {
|
|
180094
|
-
const directory =
|
|
180811
|
+
const directory = dirname5(filePath);
|
|
180095
180812
|
const existing = grouped.get(directory) ?? [];
|
|
180096
180813
|
existing.push(filePath);
|
|
180097
180814
|
grouped.set(directory, existing);
|
|
@@ -180153,8 +180870,8 @@ function discoverCodeScope(projectRoot) {
|
|
|
180153
180870
|
};
|
|
180154
180871
|
}
|
|
180155
180872
|
function discoverAgentTarget(projectRoot) {
|
|
180156
|
-
const hasAgentsDir = existsSync3(
|
|
180157
|
-
const hasClaudeDir = existsSync3(
|
|
180873
|
+
const hasAgentsDir = existsSync3(join9(projectRoot, ".agents"));
|
|
180874
|
+
const hasClaudeDir = existsSync3(join9(projectRoot, ".claude"));
|
|
180158
180875
|
let defaultTarget = "none";
|
|
180159
180876
|
let recommendedTarget = "none";
|
|
180160
180877
|
let message;
|
|
@@ -180181,10 +180898,10 @@ function discoverAgentTarget(projectRoot) {
|
|
|
180181
180898
|
};
|
|
180182
180899
|
}
|
|
180183
180900
|
function collectMarkdownFiles(projectRoot, currentDir = ".") {
|
|
180184
|
-
const absoluteDir =
|
|
180901
|
+
const absoluteDir = join9(projectRoot, currentDir);
|
|
180185
180902
|
let entries;
|
|
180186
180903
|
try {
|
|
180187
|
-
entries =
|
|
180904
|
+
entries = readdirSync3(absoluteDir);
|
|
180188
180905
|
} catch {
|
|
180189
180906
|
return [];
|
|
180190
180907
|
}
|
|
@@ -180195,7 +180912,7 @@ function collectMarkdownFiles(projectRoot, currentDir = ".") {
|
|
|
180195
180912
|
if (segments.some((segment) => IGNORED_WALK_SEGMENTS.has(segment) || segment.startsWith("."))) {
|
|
180196
180913
|
continue;
|
|
180197
180914
|
}
|
|
180198
|
-
const absolutePath =
|
|
180915
|
+
const absolutePath = join9(projectRoot, relPath);
|
|
180199
180916
|
let stats;
|
|
180200
180917
|
try {
|
|
180201
180918
|
stats = statSync2(absolutePath);
|
|
@@ -180282,6 +180999,491 @@ function isExcludedCodeFile(filePath, language) {
|
|
|
180282
180999
|
return lower.includes(".generated.");
|
|
180283
181000
|
}
|
|
180284
181001
|
|
|
181002
|
+
// src/setup/init-plan.ts
|
|
181003
|
+
import { existsSync as existsSync4, readdirSync as readdirSync5, readFileSync as readFileSync7 } from "node:fs";
|
|
181004
|
+
import { join as join11, relative as relative3 } from "node:path";
|
|
181005
|
+
|
|
181006
|
+
// src/setup/skill-assets.ts
|
|
181007
|
+
import { cpSync, lstatSync as lstatSync2, mkdirSync as mkdirSync3, readdirSync as readdirSync4, readFileSync as readFileSync6, rmSync } from "node:fs";
|
|
181008
|
+
import { join as join10 } from "node:path";
|
|
181009
|
+
function classifyManagedPath(projectRoot, relativePath) {
|
|
181010
|
+
const segments = relativePath.split(/[/\\]/).filter((segment) => segment.length > 0);
|
|
181011
|
+
let current = projectRoot;
|
|
181012
|
+
for (const [index, segment] of segments.entries()) {
|
|
181013
|
+
current = join10(current, segment);
|
|
181014
|
+
const isLast = index === segments.length - 1;
|
|
181015
|
+
let stats;
|
|
181016
|
+
try {
|
|
181017
|
+
stats = lstatSync2(current);
|
|
181018
|
+
} catch {
|
|
181019
|
+
return "absent";
|
|
181020
|
+
}
|
|
181021
|
+
if (stats.isSymbolicLink()) {
|
|
181022
|
+
return isLast ? "symlink" : "symlinked-parent";
|
|
181023
|
+
}
|
|
181024
|
+
if (!stats.isDirectory()) {
|
|
181025
|
+
return isLast ? "non-directory" : "blocked-parent";
|
|
181026
|
+
}
|
|
181027
|
+
}
|
|
181028
|
+
return segments.length === 0 ? "blocked-parent" : "directory";
|
|
181029
|
+
}
|
|
181030
|
+
function unmanageablePathMessage(path, kind) {
|
|
181031
|
+
switch (kind) {
|
|
181032
|
+
case "symlink":
|
|
181033
|
+
return `Skill directory ${path} is a symlink and was left in place.`;
|
|
181034
|
+
case "symlinked-parent":
|
|
181035
|
+
return `Skill directory ${path} sits under a symlinked directory and was left in place, because writing through it would leave the project root.`;
|
|
181036
|
+
case "non-directory":
|
|
181037
|
+
return `${path} exists but is not a directory, so it was left in place.`;
|
|
181038
|
+
case "blocked-parent":
|
|
181039
|
+
return `Skill directory ${path} cannot be reached because a parent path component is not a directory, so it was left in place.`;
|
|
181040
|
+
default:
|
|
181041
|
+
return `Skill directory ${path} was left in place.`;
|
|
181042
|
+
}
|
|
181043
|
+
}
|
|
181044
|
+
function listSkillFiles(directory) {
|
|
181045
|
+
const files = [];
|
|
181046
|
+
collectSkillFiles(directory, "", files);
|
|
181047
|
+
return files.toSorted();
|
|
181048
|
+
}
|
|
181049
|
+
function compareSkillTree(installedDir, templateDir) {
|
|
181050
|
+
const installedFiles = new Set(listSkillFiles(installedDir));
|
|
181051
|
+
const templateFiles = listSkillFiles(templateDir);
|
|
181052
|
+
const modified = [];
|
|
181053
|
+
const missing = [];
|
|
181054
|
+
for (const file of templateFiles) {
|
|
181055
|
+
if (!installedFiles.has(file)) {
|
|
181056
|
+
missing.push(file);
|
|
181057
|
+
continue;
|
|
181058
|
+
}
|
|
181059
|
+
installedFiles.delete(file);
|
|
181060
|
+
if (readFileOrUndefined(join10(installedDir, file)) !== readFileOrUndefined(join10(templateDir, file))) {
|
|
181061
|
+
modified.push(file);
|
|
181062
|
+
}
|
|
181063
|
+
}
|
|
181064
|
+
return { modified, missing, extra: [...installedFiles].toSorted() };
|
|
181065
|
+
}
|
|
181066
|
+
function applySkillOperation(projectRoot, packageRoot, operation) {
|
|
181067
|
+
if (operation.action !== "create" && operation.action !== "overwrite" && operation.action !== "remove") {
|
|
181068
|
+
return;
|
|
181069
|
+
}
|
|
181070
|
+
const kind = classifyManagedPath(projectRoot, operation.path);
|
|
181071
|
+
const destinationDir = join10(projectRoot, operation.path);
|
|
181072
|
+
if (operation.action === "remove") {
|
|
181073
|
+
if (kind === "directory") {
|
|
181074
|
+
rmSync(destinationDir, { recursive: true, force: true });
|
|
181075
|
+
}
|
|
181076
|
+
return;
|
|
181077
|
+
}
|
|
181078
|
+
if (kind !== "absent" && kind !== "directory") {
|
|
181079
|
+
return;
|
|
181080
|
+
}
|
|
181081
|
+
const skillName = operation.path.split(/[/\\]/).at(-1);
|
|
181082
|
+
if (skillName === undefined || skillName.length === 0) {
|
|
181083
|
+
return;
|
|
181084
|
+
}
|
|
181085
|
+
if (operation.action === "overwrite" && kind === "directory") {
|
|
181086
|
+
rmSync(destinationDir, { recursive: true, force: true });
|
|
181087
|
+
}
|
|
181088
|
+
mkdirSync3(destinationDir, { recursive: true });
|
|
181089
|
+
cpSync(join10(packageRoot, "templates", "skills", skillName), destinationDir, {
|
|
181090
|
+
recursive: true,
|
|
181091
|
+
force: true
|
|
181092
|
+
});
|
|
181093
|
+
}
|
|
181094
|
+
function collectSkillFiles(root, prefix, files) {
|
|
181095
|
+
let entries;
|
|
181096
|
+
try {
|
|
181097
|
+
entries = readdirSync4(join10(root, prefix), { withFileTypes: true });
|
|
181098
|
+
} catch {
|
|
181099
|
+
return;
|
|
181100
|
+
}
|
|
181101
|
+
for (const entry of entries.toSorted((left, right) => left.name < right.name ? -1 : 1)) {
|
|
181102
|
+
const relativePath = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
181103
|
+
if (entry.isDirectory()) {
|
|
181104
|
+
collectSkillFiles(root, relativePath, files);
|
|
181105
|
+
continue;
|
|
181106
|
+
}
|
|
181107
|
+
files.push(relativePath);
|
|
181108
|
+
}
|
|
181109
|
+
}
|
|
181110
|
+
function readFileOrUndefined(path) {
|
|
181111
|
+
try {
|
|
181112
|
+
return readFileSync6(path, "utf8");
|
|
181113
|
+
} catch {
|
|
181114
|
+
return;
|
|
181115
|
+
}
|
|
181116
|
+
}
|
|
181117
|
+
|
|
181118
|
+
// src/setup/init-plan.ts
|
|
181119
|
+
var CONFIG_FILE_NAME2 = "docbridge.config.json";
|
|
181120
|
+
var INIT_SKILL_NAMES = ["docbridge"];
|
|
181121
|
+
var LEGACY_SKILL_NAMES = [
|
|
181122
|
+
"docbridge-adopt",
|
|
181123
|
+
"docbridge-annotate",
|
|
181124
|
+
"docbridge-link",
|
|
181125
|
+
"docbridge-review",
|
|
181126
|
+
"docbridge-sync"
|
|
181127
|
+
];
|
|
181128
|
+
function planInitCommand(input) {
|
|
181129
|
+
const packageRoot = input.packageRoot ?? resolvePackageRoot();
|
|
181130
|
+
const existingConfig = readExistingConfig(input.projectRoot);
|
|
181131
|
+
const messages = [];
|
|
181132
|
+
const nextSteps = [];
|
|
181133
|
+
let exitCode = 0;
|
|
181134
|
+
const agentResolution = input.command === "init" ? resolveInitAgentTarget(input.discovery, input.options) : resolveInitWithAgentTarget(input.discovery, input.options);
|
|
181135
|
+
if (agentResolution.error !== undefined) {
|
|
181136
|
+
return {
|
|
181137
|
+
command: input.command,
|
|
181138
|
+
configOps: [],
|
|
181139
|
+
skillOps: [],
|
|
181140
|
+
messages: [agentResolution.error],
|
|
181141
|
+
nextSteps: [],
|
|
181142
|
+
agentGuidance: [],
|
|
181143
|
+
exitCode: 1
|
|
181144
|
+
};
|
|
181145
|
+
}
|
|
181146
|
+
const agentTarget = agentResolution.target ?? "none";
|
|
181147
|
+
const configOps = input.command === "init" ? planConfigOperations({
|
|
181148
|
+
projectRoot: input.projectRoot,
|
|
181149
|
+
options: input.options,
|
|
181150
|
+
discovery: input.discovery,
|
|
181151
|
+
confirmedScope: input.confirmedScope,
|
|
181152
|
+
existingConfig,
|
|
181153
|
+
messages
|
|
181154
|
+
}) : [];
|
|
181155
|
+
if (configOps.some((operation) => operation.action === "create" || operation.action === "would-create")) {
|
|
181156
|
+
nextSteps.push("Review docbridge.config.json and adjust include.docs or include.code if needed.");
|
|
181157
|
+
nextSteps.push("Add @doc and @code annotations to link code and documentation.");
|
|
181158
|
+
nextSteps.push("Run docbridge check when links exist.");
|
|
181159
|
+
} else if (existingConfig.ok) {
|
|
181160
|
+
messages.push("Existing docbridge.config.json was left unchanged.");
|
|
181161
|
+
summarizeExistingConfig(existingConfig.config, messages);
|
|
181162
|
+
} else if (existingConfig.rawText !== undefined) {
|
|
181163
|
+
messages.push("Existing docbridge.config.json is invalid and was not modified.");
|
|
181164
|
+
for (const diagnostic of existingConfig.diagnostics) {
|
|
181165
|
+
messages.push(diagnostic.message);
|
|
181166
|
+
}
|
|
181167
|
+
messages.push("Repair the config manually or delete it before re-running init.");
|
|
181168
|
+
exitCode = 1;
|
|
181169
|
+
}
|
|
181170
|
+
const skillOps = planSkillOperations({
|
|
181171
|
+
projectRoot: input.projectRoot,
|
|
181172
|
+
packageRoot,
|
|
181173
|
+
agentTarget,
|
|
181174
|
+
options: input.options,
|
|
181175
|
+
messages
|
|
181176
|
+
});
|
|
181177
|
+
const agentGuidance = input.command === "init-with-agent" && agentTarget !== "none" ? buildAgentGuidance(input.projectRoot, agentTarget) : [];
|
|
181178
|
+
if (input.command === "init-with-agent") {
|
|
181179
|
+
nextSteps.push("Run the printed one-shot command or fallback prompt in your agent.");
|
|
181180
|
+
nextSteps.push("Let the docbridge skill confirm scope and create or improve docbridge.config.json.");
|
|
181181
|
+
}
|
|
181182
|
+
if (input.options.yes && input.command === "init" && configOps.length === 0 && existingConfig.rawText === undefined && (input.discovery.docs.ambiguous || input.discovery.code.ambiguous)) {
|
|
181183
|
+
exitCode = 1;
|
|
181184
|
+
}
|
|
181185
|
+
return {
|
|
181186
|
+
command: input.command,
|
|
181187
|
+
configOps,
|
|
181188
|
+
skillOps,
|
|
181189
|
+
messages,
|
|
181190
|
+
nextSteps,
|
|
181191
|
+
agentGuidance,
|
|
181192
|
+
exitCode
|
|
181193
|
+
};
|
|
181194
|
+
}
|
|
181195
|
+
function buildConfigFromScope(scope) {
|
|
181196
|
+
const code = {};
|
|
181197
|
+
for (const language of scope.languages) {
|
|
181198
|
+
code[language.language] = { patterns: [...language.patterns] };
|
|
181199
|
+
}
|
|
181200
|
+
return {
|
|
181201
|
+
include: {
|
|
181202
|
+
code,
|
|
181203
|
+
docs: [scope.docsPattern]
|
|
181204
|
+
}
|
|
181205
|
+
};
|
|
181206
|
+
}
|
|
181207
|
+
function listDistributableSkills(packageRoot) {
|
|
181208
|
+
const skillsRoot = join11(packageRoot, "templates", "skills");
|
|
181209
|
+
let entries;
|
|
181210
|
+
try {
|
|
181211
|
+
entries = readdirSync5(skillsRoot);
|
|
181212
|
+
} catch {
|
|
181213
|
+
return [];
|
|
181214
|
+
}
|
|
181215
|
+
return entries.filter((entry) => entry.startsWith("docbridge")).filter((entry) => existsSync4(join11(skillsRoot, entry, "SKILL.md"))).toSorted();
|
|
181216
|
+
}
|
|
181217
|
+
function formatInitPlan(plan) {
|
|
181218
|
+
const lines = [];
|
|
181219
|
+
for (const message of plan.messages) {
|
|
181220
|
+
lines.push(message);
|
|
181221
|
+
}
|
|
181222
|
+
if (plan.configOps.length > 0) {
|
|
181223
|
+
lines.push("");
|
|
181224
|
+
lines.push("Config:");
|
|
181225
|
+
for (const operation of plan.configOps) {
|
|
181226
|
+
lines.push(formatFileOp(operation));
|
|
181227
|
+
if (operation.content !== undefined) {
|
|
181228
|
+
lines.push(operation.content);
|
|
181229
|
+
}
|
|
181230
|
+
}
|
|
181231
|
+
}
|
|
181232
|
+
if (plan.skillOps.length > 0) {
|
|
181233
|
+
lines.push("");
|
|
181234
|
+
lines.push("Skills:");
|
|
181235
|
+
for (const operation of plan.skillOps) {
|
|
181236
|
+
lines.push(formatFileOp(operation));
|
|
181237
|
+
}
|
|
181238
|
+
}
|
|
181239
|
+
if (plan.agentGuidance.length > 0) {
|
|
181240
|
+
lines.push("");
|
|
181241
|
+
lines.push("Agent setup:");
|
|
181242
|
+
for (const guidance of plan.agentGuidance) {
|
|
181243
|
+
lines.push("");
|
|
181244
|
+
lines.push(`${capitalize(guidance.agent)} destination: ${guidance.destination}`);
|
|
181245
|
+
lines.push(`One-shot command: ${guidance.oneShotCommand}`);
|
|
181246
|
+
lines.push("Fallback prompt:");
|
|
181247
|
+
lines.push(guidance.fallbackPrompt);
|
|
181248
|
+
}
|
|
181249
|
+
}
|
|
181250
|
+
if (plan.nextSteps.length > 0) {
|
|
181251
|
+
lines.push("");
|
|
181252
|
+
lines.push("Next steps:");
|
|
181253
|
+
for (const step of plan.nextSteps) {
|
|
181254
|
+
lines.push(`- ${step}`);
|
|
181255
|
+
}
|
|
181256
|
+
}
|
|
181257
|
+
return `${lines.join(`
|
|
181258
|
+
`)}
|
|
181259
|
+
`;
|
|
181260
|
+
}
|
|
181261
|
+
function planConfigOperations(input) {
|
|
181262
|
+
if (input.existingConfig.rawText !== undefined) {
|
|
181263
|
+
return [];
|
|
181264
|
+
}
|
|
181265
|
+
const scope = input.confirmedScope ?? inferConfirmedScope(input.discovery, input.options);
|
|
181266
|
+
if (scope === undefined) {
|
|
181267
|
+
if (input.discovery.docs.message !== undefined) {
|
|
181268
|
+
input.messages.push(input.discovery.docs.message);
|
|
181269
|
+
}
|
|
181270
|
+
if (input.discovery.code.message !== undefined) {
|
|
181271
|
+
input.messages.push(input.discovery.code.message);
|
|
181272
|
+
}
|
|
181273
|
+
if (input.options.yes) {
|
|
181274
|
+
input.messages.push("Automatic setup stopped because docs or code scope is ambiguous. Re-run interactively or confirm scope explicitly.");
|
|
181275
|
+
}
|
|
181276
|
+
return [];
|
|
181277
|
+
}
|
|
181278
|
+
const config = buildConfigFromScope(scope);
|
|
181279
|
+
const content = `${JSON.stringify({ $schema: "./schemas/docbridge.schema.json", ...config }, null, 2)}
|
|
181280
|
+
`;
|
|
181281
|
+
const action = input.options.dryRun ? "would-create" : "create";
|
|
181282
|
+
input.messages.push(`Docs scope: ${scope.docsPattern}`);
|
|
181283
|
+
input.messages.push(`Code scope: ${scope.languages.map((language) => `${language.language} (${language.patterns.join(", ")})`).join("; ")}`);
|
|
181284
|
+
return [
|
|
181285
|
+
{
|
|
181286
|
+
action,
|
|
181287
|
+
path: CONFIG_FILE_NAME2,
|
|
181288
|
+
content,
|
|
181289
|
+
reason: "Create docbridge.config.json from discovered scope."
|
|
181290
|
+
}
|
|
181291
|
+
];
|
|
181292
|
+
}
|
|
181293
|
+
function inferConfirmedScope(discovery, options) {
|
|
181294
|
+
if (discovery.docs.ambiguous || discovery.code.ambiguous) {
|
|
181295
|
+
return;
|
|
181296
|
+
}
|
|
181297
|
+
if (discovery.docs.recommended === undefined || discovery.code.languages.length === 0) {
|
|
181298
|
+
return;
|
|
181299
|
+
}
|
|
181300
|
+
if (!options.yes) {
|
|
181301
|
+
return;
|
|
181302
|
+
}
|
|
181303
|
+
return {
|
|
181304
|
+
docsPattern: discovery.docs.recommended.pattern,
|
|
181305
|
+
languages: discovery.code.languages
|
|
181306
|
+
};
|
|
181307
|
+
}
|
|
181308
|
+
function planSkillOperations(input) {
|
|
181309
|
+
if (input.agentTarget === "none") {
|
|
181310
|
+
return [];
|
|
181311
|
+
}
|
|
181312
|
+
const destinations = agentDestinations(input.agentTarget);
|
|
181313
|
+
const availableSkills = new Set(listDistributableSkills(input.packageRoot));
|
|
181314
|
+
const operations = [];
|
|
181315
|
+
for (const destination of destinations) {
|
|
181316
|
+
for (const skillName of INIT_SKILL_NAMES) {
|
|
181317
|
+
if (!availableSkills.has(skillName)) {
|
|
181318
|
+
continue;
|
|
181319
|
+
}
|
|
181320
|
+
const destinationDir = join11(input.projectRoot, destination, skillName);
|
|
181321
|
+
const path = relative3(input.projectRoot, destinationDir);
|
|
181322
|
+
const kind = classifyManagedPath(input.projectRoot, path);
|
|
181323
|
+
if (kind !== "absent" && kind !== "directory") {
|
|
181324
|
+
input.messages.push(unmanageablePathMessage(path, kind));
|
|
181325
|
+
continue;
|
|
181326
|
+
}
|
|
181327
|
+
const exists = kind === "directory";
|
|
181328
|
+
let action;
|
|
181329
|
+
let reason;
|
|
181330
|
+
if (exists && input.options.force) {
|
|
181331
|
+
action = input.options.dryRun ? "would-overwrite" : "overwrite";
|
|
181332
|
+
reason = "Overwrite existing skill directory.";
|
|
181333
|
+
} else if (exists) {
|
|
181334
|
+
action = "skip";
|
|
181335
|
+
reason = "Skill directory already exists.";
|
|
181336
|
+
} else {
|
|
181337
|
+
action = input.options.dryRun ? "would-create" : "create";
|
|
181338
|
+
reason = "Install distributable skill template.";
|
|
181339
|
+
}
|
|
181340
|
+
operations.push({
|
|
181341
|
+
action,
|
|
181342
|
+
path,
|
|
181343
|
+
reason
|
|
181344
|
+
});
|
|
181345
|
+
}
|
|
181346
|
+
operations.push(...planLegacySkillOperations({
|
|
181347
|
+
projectRoot: input.projectRoot,
|
|
181348
|
+
destination,
|
|
181349
|
+
options: input.options,
|
|
181350
|
+
messages: input.messages
|
|
181351
|
+
}));
|
|
181352
|
+
}
|
|
181353
|
+
return operations;
|
|
181354
|
+
}
|
|
181355
|
+
function planLegacySkillOperations(input) {
|
|
181356
|
+
const operations = [];
|
|
181357
|
+
const leftover = [];
|
|
181358
|
+
for (const skillName of LEGACY_SKILL_NAMES) {
|
|
181359
|
+
const destinationDir = join11(input.projectRoot, input.destination, skillName);
|
|
181360
|
+
const path = relative3(input.projectRoot, destinationDir);
|
|
181361
|
+
const kind = classifyManagedPath(input.projectRoot, path);
|
|
181362
|
+
if (kind === "absent") {
|
|
181363
|
+
continue;
|
|
181364
|
+
}
|
|
181365
|
+
if (kind !== "directory") {
|
|
181366
|
+
input.messages.push(unmanageablePathMessage(path, kind));
|
|
181367
|
+
continue;
|
|
181368
|
+
}
|
|
181369
|
+
if (input.options.force) {
|
|
181370
|
+
operations.push({
|
|
181371
|
+
action: input.options.dryRun ? "would-remove" : "remove",
|
|
181372
|
+
path,
|
|
181373
|
+
reason: "Remove leftover skill from the previous five-skill layout."
|
|
181374
|
+
});
|
|
181375
|
+
} else {
|
|
181376
|
+
leftover.push(path);
|
|
181377
|
+
}
|
|
181378
|
+
}
|
|
181379
|
+
if (leftover.length > 0) {
|
|
181380
|
+
input.messages.push(`Legacy DocBridge skill directories were left in place (${leftover.join(", ")}). Re-run with --force to remove them after reviewing local edits.`);
|
|
181381
|
+
}
|
|
181382
|
+
return operations;
|
|
181383
|
+
}
|
|
181384
|
+
function buildAgentGuidance(projectRoot, agentTarget) {
|
|
181385
|
+
const guidance = [];
|
|
181386
|
+
if (agentTarget === "codex" || agentTarget === "both") {
|
|
181387
|
+
guidance.push({
|
|
181388
|
+
agent: "codex",
|
|
181389
|
+
destination: ".agents/skills/docbridge/",
|
|
181390
|
+
oneShotCommand: `cd ${projectRoot} && codex --prompt "Use the docbridge skill to adopt DocBridge in this repository."`,
|
|
181391
|
+
fallbackPrompt: buildFallbackPrompt(projectRoot, "codex")
|
|
181392
|
+
});
|
|
181393
|
+
}
|
|
181394
|
+
if (agentTarget === "claude" || agentTarget === "both") {
|
|
181395
|
+
guidance.push({
|
|
181396
|
+
agent: "claude",
|
|
181397
|
+
destination: ".claude/skills/docbridge/",
|
|
181398
|
+
oneShotCommand: `cd ${projectRoot} && /docbridge adopt DocBridge in this repository`,
|
|
181399
|
+
fallbackPrompt: buildFallbackPrompt(projectRoot, "claude")
|
|
181400
|
+
});
|
|
181401
|
+
}
|
|
181402
|
+
return guidance;
|
|
181403
|
+
}
|
|
181404
|
+
function buildFallbackPrompt(projectRoot, agent) {
|
|
181405
|
+
const invocation = agent === "claude" ? "Use the docbridge skill" : "Use the docbridge skill from .agents/skills/docbridge";
|
|
181406
|
+
return `${invocation} to adopt DocBridge in ${projectRoot}. Confirm docs and code scope, create or improve docbridge.config.json, and suggest the next linking steps.`;
|
|
181407
|
+
}
|
|
181408
|
+
function resolveInitAgentTarget(discovery, options) {
|
|
181409
|
+
if (options.agentTarget !== undefined) {
|
|
181410
|
+
return { target: options.agentTarget, error: undefined };
|
|
181411
|
+
}
|
|
181412
|
+
if (options.yes) {
|
|
181413
|
+
return { target: discovery.agent.defaultTarget, error: undefined };
|
|
181414
|
+
}
|
|
181415
|
+
return { target: discovery.agent.recommendedTarget, error: undefined };
|
|
181416
|
+
}
|
|
181417
|
+
function resolveInitWithAgentTarget(discovery, options) {
|
|
181418
|
+
if (options.agentTarget !== undefined) {
|
|
181419
|
+
if (options.agentTarget === "none") {
|
|
181420
|
+
return {
|
|
181421
|
+
target: undefined,
|
|
181422
|
+
error: "init-with-agent requires an agent target other than none."
|
|
181423
|
+
};
|
|
181424
|
+
}
|
|
181425
|
+
return { target: options.agentTarget, error: undefined };
|
|
181426
|
+
}
|
|
181427
|
+
if (discovery.agent.defaultTarget === "none") {
|
|
181428
|
+
if (options.yes) {
|
|
181429
|
+
return {
|
|
181430
|
+
target: undefined,
|
|
181431
|
+
error: "init-with-agent --yes requires an explicit agent target when no .agents/ or .claude/ directory exists. Pass --agent-target codex, claude, or both."
|
|
181432
|
+
};
|
|
181433
|
+
}
|
|
181434
|
+
return { target: discovery.agent.recommendedTarget, error: undefined };
|
|
181435
|
+
}
|
|
181436
|
+
return { target: discovery.agent.defaultTarget, error: undefined };
|
|
181437
|
+
}
|
|
181438
|
+
function readExistingConfig(projectRoot) {
|
|
181439
|
+
const configPath = join11(projectRoot, CONFIG_FILE_NAME2);
|
|
181440
|
+
let rawText;
|
|
181441
|
+
try {
|
|
181442
|
+
rawText = readFileSync7(configPath, "utf8");
|
|
181443
|
+
} catch {
|
|
181444
|
+
rawText = undefined;
|
|
181445
|
+
}
|
|
181446
|
+
const resolved = resolveConfig(rawText);
|
|
181447
|
+
return {
|
|
181448
|
+
rawText,
|
|
181449
|
+
ok: resolved.ok,
|
|
181450
|
+
config: resolved.config,
|
|
181451
|
+
diagnostics: resolved.diagnostics
|
|
181452
|
+
};
|
|
181453
|
+
}
|
|
181454
|
+
function summarizeExistingConfig(config, messages) {
|
|
181455
|
+
messages.push(`Docs scope: ${config.include.docs.join(", ")}`);
|
|
181456
|
+
const codeSummary = Object.entries(config.include.code).map(([language, entry]) => `${language}: ${entry?.patterns.join(", ") ?? ""}`).join("; ");
|
|
181457
|
+
messages.push(`Code scope: ${codeSummary}`);
|
|
181458
|
+
}
|
|
181459
|
+
function agentDestinations(agentTarget) {
|
|
181460
|
+
switch (agentTarget) {
|
|
181461
|
+
case "codex":
|
|
181462
|
+
return [".agents/skills"];
|
|
181463
|
+
case "claude":
|
|
181464
|
+
return [".claude/skills"];
|
|
181465
|
+
case "both":
|
|
181466
|
+
return [".agents/skills", ".claude/skills"];
|
|
181467
|
+
default:
|
|
181468
|
+
return [];
|
|
181469
|
+
}
|
|
181470
|
+
}
|
|
181471
|
+
function formatFileOp(operation) {
|
|
181472
|
+
let label = operation.action;
|
|
181473
|
+
if (operation.action === "would-create") {
|
|
181474
|
+
label = "would create";
|
|
181475
|
+
} else if (operation.action === "would-overwrite") {
|
|
181476
|
+
label = "would overwrite";
|
|
181477
|
+
} else if (operation.action === "would-remove") {
|
|
181478
|
+
label = "would remove";
|
|
181479
|
+
}
|
|
181480
|
+
const reason = operation.reason ? ` (${operation.reason})` : "";
|
|
181481
|
+
return `- ${label} ${operation.path}${reason}`;
|
|
181482
|
+
}
|
|
181483
|
+
function capitalize(value) {
|
|
181484
|
+
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
181485
|
+
}
|
|
181486
|
+
|
|
180285
181487
|
// src/cli/init.ts
|
|
180286
181488
|
var AGENT_TARGETS = new Set(["codex", "claude", "both", "none"]);
|
|
180287
181489
|
function parseInitOptions(args, command) {
|
|
@@ -180348,7 +181550,7 @@ function createDefaultPrompts(isInteractive = process.stdin.isTTY) {
|
|
|
180348
181550
|
return defaultValue;
|
|
180349
181551
|
}
|
|
180350
181552
|
process.stdout.write(`${message} ${defaultValue ? "[Y/n]" : "[y/N]"} `);
|
|
180351
|
-
const answer =
|
|
181553
|
+
const answer = readFileSync8(0, "utf8").trim().toLowerCase();
|
|
180352
181554
|
if (answer.length === 0) {
|
|
180353
181555
|
return defaultValue;
|
|
180354
181556
|
}
|
|
@@ -180365,7 +181567,7 @@ function createDefaultPrompts(isInteractive = process.stdin.isTTY) {
|
|
|
180365
181567
|
`);
|
|
180366
181568
|
}
|
|
180367
181569
|
process.stdout.write(`Choice [${defaultChoice}]: `);
|
|
180368
|
-
const answer =
|
|
181570
|
+
const answer = readFileSync8(0, "utf8").trim();
|
|
180369
181571
|
if (answer.length === 0) {
|
|
180370
181572
|
return defaultChoice;
|
|
180371
181573
|
}
|
|
@@ -180481,30 +181683,17 @@ function executePlan(projectRoot, plan, packageRoot) {
|
|
|
180481
181683
|
if (operation.action === "skip") {
|
|
180482
181684
|
continue;
|
|
180483
181685
|
}
|
|
180484
|
-
|
|
181686
|
+
applySkillOperation(projectRoot, packageRoot, operation);
|
|
180485
181687
|
}
|
|
180486
181688
|
}
|
|
180487
181689
|
function executeFileOperation(projectRoot, operation) {
|
|
180488
|
-
const absolutePath =
|
|
181690
|
+
const absolutePath = join12(projectRoot, operation.path);
|
|
180489
181691
|
if (operation.action === "create" && operation.content !== undefined) {
|
|
180490
|
-
|
|
181692
|
+
writeFileSync2(absolutePath, operation.content, "utf8");
|
|
180491
181693
|
}
|
|
180492
181694
|
if (operation.action === "overwrite" && operation.content !== undefined) {
|
|
180493
|
-
|
|
180494
|
-
}
|
|
180495
|
-
}
|
|
180496
|
-
function executeSkillOperation(projectRoot, packageRoot, operation) {
|
|
180497
|
-
if (operation.action !== "create" && operation.action !== "overwrite") {
|
|
180498
|
-
return;
|
|
180499
|
-
}
|
|
180500
|
-
const skillName = operation.path.split("/").at(-1);
|
|
180501
|
-
if (skillName === undefined) {
|
|
180502
|
-
return;
|
|
181695
|
+
writeFileSync2(absolutePath, operation.content, "utf8");
|
|
180503
181696
|
}
|
|
180504
|
-
const sourceDir = join8(packageRoot, "templates", "skills", skillName);
|
|
180505
|
-
const destinationDir = join8(projectRoot, operation.path);
|
|
180506
|
-
mkdirSync2(destinationDir, { recursive: true });
|
|
180507
|
-
cpSync(sourceDir, destinationDir, { recursive: true, force: true });
|
|
180508
181697
|
}
|
|
180509
181698
|
function resolveProjectRoot(root, command) {
|
|
180510
181699
|
const projectRoot = resolve3(root);
|
|
@@ -180520,6 +181709,343 @@ function resolveProjectRoot(root, command) {
|
|
|
180520
181709
|
return projectRoot;
|
|
180521
181710
|
}
|
|
180522
181711
|
|
|
181712
|
+
// src/cli/upgrade.ts
|
|
181713
|
+
import { statSync as statSync4 } from "node:fs";
|
|
181714
|
+
import { resolve as resolve4 } from "node:path";
|
|
181715
|
+
|
|
181716
|
+
// src/setup/upgrade-plan.ts
|
|
181717
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
181718
|
+
import { join as join13, relative as relative4 } from "node:path";
|
|
181719
|
+
var MANAGED_SKILL_NAME = INIT_SKILL_NAMES[0];
|
|
181720
|
+
function reportCliVersion(currentVersion, latest) {
|
|
181721
|
+
if (latest.status !== "ok") {
|
|
181722
|
+
return { current: currentVersion, latest: undefined, status: "unknown" };
|
|
181723
|
+
}
|
|
181724
|
+
if (isNewerStableVersion(latest.latest, currentVersion)) {
|
|
181725
|
+
return { current: currentVersion, latest: latest.latest, status: "outdated" };
|
|
181726
|
+
}
|
|
181727
|
+
if (isNewerStableVersion(currentVersion, latest.latest)) {
|
|
181728
|
+
return { current: currentVersion, latest: latest.latest, status: "ahead" };
|
|
181729
|
+
}
|
|
181730
|
+
return { current: currentVersion, latest: latest.latest, status: "up-to-date" };
|
|
181731
|
+
}
|
|
181732
|
+
function resolveUpgradeAgentTarget(discovery, explicitTarget) {
|
|
181733
|
+
return explicitTarget ?? discovery.agent.defaultTarget;
|
|
181734
|
+
}
|
|
181735
|
+
function inspectManagedSkill(input) {
|
|
181736
|
+
const destinationDir = join13(input.projectRoot, input.destination, MANAGED_SKILL_NAME);
|
|
181737
|
+
const path = toRootRelative(input.projectRoot, destinationDir);
|
|
181738
|
+
const empty = {
|
|
181739
|
+
destination: input.destination,
|
|
181740
|
+
path,
|
|
181741
|
+
modifiedFiles: [],
|
|
181742
|
+
missingFiles: [],
|
|
181743
|
+
extraFiles: []
|
|
181744
|
+
};
|
|
181745
|
+
const kind = classifyManagedPath(input.projectRoot, path);
|
|
181746
|
+
if (kind !== "directory") {
|
|
181747
|
+
return { ...empty, state: kind };
|
|
181748
|
+
}
|
|
181749
|
+
if (!input.templateAvailable) {
|
|
181750
|
+
return { ...empty, state: "template-missing" };
|
|
181751
|
+
}
|
|
181752
|
+
const comparison = compareSkillTree(destinationDir, join13(input.packageRoot, "templates", "skills", MANAGED_SKILL_NAME));
|
|
181753
|
+
const drifted = comparison.modified.length > 0 || comparison.missing.length > 0 || comparison.extra.length > 0;
|
|
181754
|
+
return {
|
|
181755
|
+
destination: input.destination,
|
|
181756
|
+
path,
|
|
181757
|
+
state: drifted ? "modified" : "up-to-date",
|
|
181758
|
+
modifiedFiles: comparison.modified,
|
|
181759
|
+
missingFiles: comparison.missing,
|
|
181760
|
+
extraFiles: comparison.extra
|
|
181761
|
+
};
|
|
181762
|
+
}
|
|
181763
|
+
function inspectLegacySkills(projectRoot, destination) {
|
|
181764
|
+
const reports = [];
|
|
181765
|
+
for (const skillName of LEGACY_SKILL_NAMES) {
|
|
181766
|
+
const destinationDir = join13(projectRoot, destination, skillName);
|
|
181767
|
+
const path = toRootRelative(projectRoot, destinationDir);
|
|
181768
|
+
const kind = classifyManagedPath(projectRoot, path);
|
|
181769
|
+
if (kind === "absent") {
|
|
181770
|
+
continue;
|
|
181771
|
+
}
|
|
181772
|
+
if (kind === "symlinked-parent" && !existsSync5(destinationDir)) {
|
|
181773
|
+
continue;
|
|
181774
|
+
}
|
|
181775
|
+
reports.push({ path, kind });
|
|
181776
|
+
}
|
|
181777
|
+
return reports;
|
|
181778
|
+
}
|
|
181779
|
+
function planUpgrade(input) {
|
|
181780
|
+
const mode = input.options.check ? "check" : "apply";
|
|
181781
|
+
const cli = reportCliVersion(input.currentVersion, input.latest);
|
|
181782
|
+
const agentTarget = resolveUpgradeAgentTarget(input.discovery, input.options.agentTarget);
|
|
181783
|
+
const templateAvailable = listDistributableSkills(input.packageRoot).includes(MANAGED_SKILL_NAME);
|
|
181784
|
+
const messages = [];
|
|
181785
|
+
const destinations = agentDestinations(agentTarget);
|
|
181786
|
+
const managedSkills = destinations.map((destination) => inspectManagedSkill({
|
|
181787
|
+
projectRoot: input.projectRoot,
|
|
181788
|
+
packageRoot: input.packageRoot,
|
|
181789
|
+
destination,
|
|
181790
|
+
templateAvailable
|
|
181791
|
+
}));
|
|
181792
|
+
const legacySkills = destinations.flatMap((destination) => inspectLegacySkills(input.projectRoot, destination));
|
|
181793
|
+
if (destinations.length === 0) {
|
|
181794
|
+
messages.push("No agent skill directory was selected, so no managed assets were inspected. Pass --agent-target codex, claude, or both to reconcile them.");
|
|
181795
|
+
}
|
|
181796
|
+
if (!templateAvailable && destinations.length > 0) {
|
|
181797
|
+
messages.push("The installed DocBridge package ships no docbridge skill template, so managed skills cannot be reconciled. Reinstall DocBridge.");
|
|
181798
|
+
}
|
|
181799
|
+
addVersionMessages(cli, input.guidance, messages);
|
|
181800
|
+
const pending = [];
|
|
181801
|
+
const operations = mode === "check" ? [] : planOperations({ managedSkills, legacySkills, options: input.options, pending });
|
|
181802
|
+
for (const report of managedSkills) {
|
|
181803
|
+
if (isUnmanageableState(report.state)) {
|
|
181804
|
+
messages.push(unmanageablePathMessage(report.path, report.state));
|
|
181805
|
+
}
|
|
181806
|
+
}
|
|
181807
|
+
for (const legacy of legacySkills) {
|
|
181808
|
+
if (legacy.kind !== "directory") {
|
|
181809
|
+
messages.push(unmanageablePathMessage(legacy.path, legacy.kind));
|
|
181810
|
+
}
|
|
181811
|
+
}
|
|
181812
|
+
return {
|
|
181813
|
+
mode,
|
|
181814
|
+
cli,
|
|
181815
|
+
guidance: input.guidance,
|
|
181816
|
+
managedSkills,
|
|
181817
|
+
legacySkills,
|
|
181818
|
+
operations,
|
|
181819
|
+
pending,
|
|
181820
|
+
messages,
|
|
181821
|
+
nextSteps: buildNextSteps({ mode, cli, options: input.options, pending }),
|
|
181822
|
+
requiresConfirmation: mode === "apply" && !input.options.dryRun && !input.options.yes && operations.some((operation) => operation.action === "overwrite" || operation.action === "remove"),
|
|
181823
|
+
exitCode: 0
|
|
181824
|
+
};
|
|
181825
|
+
}
|
|
181826
|
+
function planOperations(input) {
|
|
181827
|
+
const operations = [];
|
|
181828
|
+
for (const report of input.managedSkills) {
|
|
181829
|
+
if (report.state === "template-missing" || isUnmanageableState(report.state)) {
|
|
181830
|
+
continue;
|
|
181831
|
+
}
|
|
181832
|
+
if (report.state === "absent") {
|
|
181833
|
+
operations.push({
|
|
181834
|
+
action: input.options.dryRun ? "would-create" : "create",
|
|
181835
|
+
path: report.path,
|
|
181836
|
+
reason: "Install the managed docbridge skill."
|
|
181837
|
+
});
|
|
181838
|
+
continue;
|
|
181839
|
+
}
|
|
181840
|
+
if (!input.options.force) {
|
|
181841
|
+
input.pending.push(`${report.path} is ${report.state === "modified" ? "locally modified" : "already installed"} and was preserved. Re-run with --force to replace it with the packaged template.`);
|
|
181842
|
+
continue;
|
|
181843
|
+
}
|
|
181844
|
+
operations.push({
|
|
181845
|
+
action: input.options.dryRun ? "would-overwrite" : "overwrite",
|
|
181846
|
+
path: report.path,
|
|
181847
|
+
reason: "Replace the managed docbridge skill with the packaged template."
|
|
181848
|
+
});
|
|
181849
|
+
}
|
|
181850
|
+
for (const legacy of input.legacySkills) {
|
|
181851
|
+
if (legacy.kind !== "directory") {
|
|
181852
|
+
continue;
|
|
181853
|
+
}
|
|
181854
|
+
if (!input.options.force) {
|
|
181855
|
+
input.pending.push(`${legacy.path} is a leftover directory from the previous five-skill layout. Re-run with --force to remove it after reviewing local edits.`);
|
|
181856
|
+
continue;
|
|
181857
|
+
}
|
|
181858
|
+
operations.push({
|
|
181859
|
+
action: input.options.dryRun ? "would-remove" : "remove",
|
|
181860
|
+
path: legacy.path,
|
|
181861
|
+
reason: "Remove leftover skill from the previous five-skill layout."
|
|
181862
|
+
});
|
|
181863
|
+
}
|
|
181864
|
+
return operations;
|
|
181865
|
+
}
|
|
181866
|
+
function isUnmanageableState(state) {
|
|
181867
|
+
return state === "symlink" || state === "symlinked-parent" || state === "non-directory" || state === "blocked-parent";
|
|
181868
|
+
}
|
|
181869
|
+
function addVersionMessages(cli, guidance, messages) {
|
|
181870
|
+
if (cli.status === "outdated") {
|
|
181871
|
+
messages.push(`The running binary is older than the latest stable release. DocBridge does not upgrade itself: run \`${guidance.primaryCommand}\`, then re-run \`docbridge upgrade\` so managed assets are reconciled from the new binary.`);
|
|
181872
|
+
return;
|
|
181873
|
+
}
|
|
181874
|
+
if (cli.status === "unknown") {
|
|
181875
|
+
messages.push("The latest stable version could not be determined, so only local asset state is reported.");
|
|
181876
|
+
}
|
|
181877
|
+
}
|
|
181878
|
+
function buildNextSteps(input) {
|
|
181879
|
+
const nextSteps = [];
|
|
181880
|
+
if (input.cli.status === "outdated") {
|
|
181881
|
+
nextSteps.push("Upgrade the CLI with the command above, then re-run `docbridge upgrade`.");
|
|
181882
|
+
}
|
|
181883
|
+
if (input.mode === "check") {
|
|
181884
|
+
nextSteps.push("Run `docbridge upgrade --dry-run` to see the operations that would be applied.");
|
|
181885
|
+
}
|
|
181886
|
+
if (input.pending.length > 0) {
|
|
181887
|
+
nextSteps.push("Re-run with --force to apply the pending migrations listed above.");
|
|
181888
|
+
}
|
|
181889
|
+
if (input.mode === "apply" && input.options.dryRun) {
|
|
181890
|
+
nextSteps.push("Re-run without --dry-run to apply the plan.");
|
|
181891
|
+
}
|
|
181892
|
+
return nextSteps;
|
|
181893
|
+
}
|
|
181894
|
+
function formatUpgradePlan(plan) {
|
|
181895
|
+
const lines = [
|
|
181896
|
+
`DocBridge ${plan.cli.current} (latest stable: ${plan.cli.latest ?? "unknown"})`,
|
|
181897
|
+
`Status: ${plan.cli.status}`,
|
|
181898
|
+
...formatUpgradeGuidance(plan.guidance)
|
|
181899
|
+
];
|
|
181900
|
+
if (plan.messages.length > 0) {
|
|
181901
|
+
lines.push("", ...plan.messages);
|
|
181902
|
+
}
|
|
181903
|
+
appendSection(lines, "Managed skills:", plan.managedSkills.flatMap(formatManagedSkill));
|
|
181904
|
+
appendSection(lines, "Legacy skills:", plan.legacySkills.map((legacy) => `- ${legacy.path} (${legacy.kind})`));
|
|
181905
|
+
appendSection(lines, "Operations:", plan.operations.map((operation) => formatFileOp(operation)));
|
|
181906
|
+
appendSection(lines, "Pending migration:", plan.pending.map((entry) => `- ${entry}`));
|
|
181907
|
+
appendSection(lines, "Next steps:", plan.nextSteps.map((step) => `- ${step}`));
|
|
181908
|
+
return `${lines.join(`
|
|
181909
|
+
`)}
|
|
181910
|
+
`;
|
|
181911
|
+
}
|
|
181912
|
+
function formatManagedSkill(report) {
|
|
181913
|
+
const lines = [`- ${report.path}: ${report.state}`];
|
|
181914
|
+
appendFileList(lines, "changed", report.modifiedFiles);
|
|
181915
|
+
appendFileList(lines, "missing", report.missingFiles);
|
|
181916
|
+
appendFileList(lines, "extra", report.extraFiles);
|
|
181917
|
+
return lines;
|
|
181918
|
+
}
|
|
181919
|
+
function appendFileList(lines, label, files) {
|
|
181920
|
+
if (files.length > 0) {
|
|
181921
|
+
lines.push(` ${label}: ${files.join(", ")}`);
|
|
181922
|
+
}
|
|
181923
|
+
}
|
|
181924
|
+
function appendSection(lines, heading, entries) {
|
|
181925
|
+
if (entries.length === 0) {
|
|
181926
|
+
return;
|
|
181927
|
+
}
|
|
181928
|
+
lines.push("", heading, ...entries);
|
|
181929
|
+
}
|
|
181930
|
+
function toRootRelative(projectRoot, absolutePath) {
|
|
181931
|
+
return relative4(projectRoot, absolutePath).split("\\").join("/");
|
|
181932
|
+
}
|
|
181933
|
+
|
|
181934
|
+
// src/cli/upgrade.ts
|
|
181935
|
+
var AGENT_TARGETS2 = new Set(["codex", "claude", "both", "none"]);
|
|
181936
|
+
function parseUpgradeOptions(args) {
|
|
181937
|
+
const options = {
|
|
181938
|
+
root: ".",
|
|
181939
|
+
agentTarget: undefined,
|
|
181940
|
+
check: false,
|
|
181941
|
+
dryRun: false,
|
|
181942
|
+
yes: false,
|
|
181943
|
+
force: false
|
|
181944
|
+
};
|
|
181945
|
+
for (let index = 0;index < args.length; index += 1) {
|
|
181946
|
+
const arg = args[index];
|
|
181947
|
+
if (arg === undefined) {
|
|
181948
|
+
continue;
|
|
181949
|
+
}
|
|
181950
|
+
if (arg === "--check") {
|
|
181951
|
+
options.check = true;
|
|
181952
|
+
continue;
|
|
181953
|
+
}
|
|
181954
|
+
if (arg === "--dry-run") {
|
|
181955
|
+
options.dryRun = true;
|
|
181956
|
+
continue;
|
|
181957
|
+
}
|
|
181958
|
+
if (arg === "--yes") {
|
|
181959
|
+
options.yes = true;
|
|
181960
|
+
continue;
|
|
181961
|
+
}
|
|
181962
|
+
if (arg === "--force") {
|
|
181963
|
+
options.force = true;
|
|
181964
|
+
continue;
|
|
181965
|
+
}
|
|
181966
|
+
if (arg === "--root") {
|
|
181967
|
+
const root = args[index + 1];
|
|
181968
|
+
if (root === undefined) {
|
|
181969
|
+
throw new CliError("--root requires a path.", rootPathGuidance("upgrade"));
|
|
181970
|
+
}
|
|
181971
|
+
options.root = root;
|
|
181972
|
+
index += 1;
|
|
181973
|
+
continue;
|
|
181974
|
+
}
|
|
181975
|
+
if (arg === "--agent-target") {
|
|
181976
|
+
options.agentTarget = parseAgentTarget(args[index + 1]);
|
|
181977
|
+
index += 1;
|
|
181978
|
+
continue;
|
|
181979
|
+
}
|
|
181980
|
+
if (arg.startsWith("--")) {
|
|
181981
|
+
throw new CliError(`Unknown option: ${arg}`, commandHelpGuidance("upgrade"));
|
|
181982
|
+
}
|
|
181983
|
+
throw new CliError(`Unexpected argument: ${arg}`, commandHelpGuidance("upgrade"));
|
|
181984
|
+
}
|
|
181985
|
+
return options;
|
|
181986
|
+
}
|
|
181987
|
+
function runUpgrade(options, io, initRuntime, runtime) {
|
|
181988
|
+
const projectRoot = resolveProjectRoot2(options.root);
|
|
181989
|
+
const packageRoot = initRuntime.packageRoot ?? resolvePackageRoot();
|
|
181990
|
+
const plan = planUpgrade({
|
|
181991
|
+
projectRoot,
|
|
181992
|
+
packageRoot,
|
|
181993
|
+
currentVersion: runtime.currentVersion,
|
|
181994
|
+
latest: runtime.latest,
|
|
181995
|
+
guidance: detectUpgradeGuidance({
|
|
181996
|
+
packageRoot,
|
|
181997
|
+
projectRoot,
|
|
181998
|
+
...runtime.currentDirectory !== undefined ? { currentDirectory: runtime.currentDirectory } : {},
|
|
181999
|
+
...runtime.env !== undefined ? { env: runtime.env } : {}
|
|
182000
|
+
}),
|
|
182001
|
+
discovery: discoverRepository(projectRoot),
|
|
182002
|
+
options
|
|
182003
|
+
});
|
|
182004
|
+
if (plan.mode === "check" || options.dryRun) {
|
|
182005
|
+
io.stdout(formatUpgradePlan(plan));
|
|
182006
|
+
return plan.exitCode;
|
|
182007
|
+
}
|
|
182008
|
+
return applyUpgrade(projectRoot, packageRoot, plan, io, initRuntime);
|
|
182009
|
+
}
|
|
182010
|
+
function applyUpgrade(projectRoot, packageRoot, plan, io, initRuntime) {
|
|
182011
|
+
if (plan.requiresConfirmation && !initRuntime.prompts.isInteractive) {
|
|
182012
|
+
throw new CliError("Replacing or removing managed skill directories requires confirmation.", upgradeConfirmationGuidance());
|
|
182013
|
+
}
|
|
182014
|
+
if (plan.requiresConfirmation && !initRuntime.prompts.confirm(`Apply ${plan.operations.length} operation(s) to managed skill directories?`, false)) {
|
|
182015
|
+
io.stdout(formatUpgradePlan(plan));
|
|
182016
|
+
io.stdout(`No changes were applied.
|
|
182017
|
+
`);
|
|
182018
|
+
return 0;
|
|
182019
|
+
}
|
|
182020
|
+
for (const operation of plan.operations) {
|
|
182021
|
+
applySkillOperation(projectRoot, packageRoot, operation);
|
|
182022
|
+
}
|
|
182023
|
+
io.stdout(formatUpgradePlan(plan));
|
|
182024
|
+
return plan.exitCode;
|
|
182025
|
+
}
|
|
182026
|
+
function parseAgentTarget(value) {
|
|
182027
|
+
if (value === undefined) {
|
|
182028
|
+
throw new CliError("--agent-target requires a value.", agentTargetGuidance("upgrade"));
|
|
182029
|
+
}
|
|
182030
|
+
if (!AGENT_TARGETS2.has(value)) {
|
|
182031
|
+
throw new CliError(`Unknown agent target: ${value}. Supported values: codex, claude, both, none.`, agentTargetGuidance("upgrade"));
|
|
182032
|
+
}
|
|
182033
|
+
return value;
|
|
182034
|
+
}
|
|
182035
|
+
function resolveProjectRoot2(root) {
|
|
182036
|
+
const projectRoot = resolve4(root);
|
|
182037
|
+
let stats;
|
|
182038
|
+
try {
|
|
182039
|
+
stats = statSync4(projectRoot);
|
|
182040
|
+
} catch {
|
|
182041
|
+
throw new CliError(`Root path does not exist: ${root}`, rootPathGuidance("upgrade"));
|
|
182042
|
+
}
|
|
182043
|
+
if (!stats.isDirectory()) {
|
|
182044
|
+
throw new CliError(`Root path is not a directory: ${root}`, rootPathGuidance("upgrade"));
|
|
182045
|
+
}
|
|
182046
|
+
return projectRoot;
|
|
182047
|
+
}
|
|
182048
|
+
|
|
180523
182049
|
// src/cli/index.ts
|
|
180524
182050
|
var VERSION = package_default.version;
|
|
180525
182051
|
function parseCheckOptions(args) {
|
|
@@ -180534,11 +182060,11 @@ function parseContextOptions(args) {
|
|
|
180534
182060
|
function parseGraphOptions(args) {
|
|
180535
182061
|
return parseCommandOptions("graph", args);
|
|
180536
182062
|
}
|
|
180537
|
-
function
|
|
180538
|
-
const projectRoot =
|
|
182063
|
+
function resolveProjectRoot3(root, command) {
|
|
182064
|
+
const projectRoot = resolve5(root);
|
|
180539
182065
|
let stats;
|
|
180540
182066
|
try {
|
|
180541
|
-
stats =
|
|
182067
|
+
stats = statSync5(projectRoot);
|
|
180542
182068
|
} catch {
|
|
180543
182069
|
throw new CliError(`Root path does not exist: ${root}`, rootPathGuidance(command));
|
|
180544
182070
|
}
|
|
@@ -180548,7 +182074,7 @@ function resolveProjectRoot2(root, command) {
|
|
|
180548
182074
|
return projectRoot;
|
|
180549
182075
|
}
|
|
180550
182076
|
function runCheck(options, io) {
|
|
180551
|
-
const projectRoot =
|
|
182077
|
+
const projectRoot = resolveProjectRoot3(options.root, "check");
|
|
180552
182078
|
const result = check({ projectRoot, audit: options.audit });
|
|
180553
182079
|
if (options.json) {
|
|
180554
182080
|
io.stdout(`${JSON.stringify(result, null, 2)}
|
|
@@ -180560,9 +182086,10 @@ function runCheck(options, io) {
|
|
|
180560
182086
|
|
|
180561
182087
|
` : "";
|
|
180562
182088
|
io.stdout(`${body}${formatSummary(result.summary)}
|
|
182089
|
+
See \`docbridge docs show troubleshooting\` for diagnostic codes and fixes.
|
|
180563
182090
|
`);
|
|
180564
|
-
|
|
180565
|
-
|
|
182091
|
+
const guidance = repairGuidanceFor(result.diagnostics, projectRoot);
|
|
182092
|
+
if (guidance !== undefined) {
|
|
180566
182093
|
io.stderr(`${guidance}
|
|
180567
182094
|
`);
|
|
180568
182095
|
}
|
|
@@ -180570,10 +182097,10 @@ function runCheck(options, io) {
|
|
|
180570
182097
|
return result.summary.errors > 0 ? 1 : 0;
|
|
180571
182098
|
}
|
|
180572
182099
|
function runFileCommand(command, options, io, execute, render) {
|
|
180573
|
-
const projectRoot =
|
|
182100
|
+
const projectRoot = resolveProjectRoot3(options.root, command);
|
|
180574
182101
|
const inputFiles = [...options.files];
|
|
180575
182102
|
if (options.stdin) {
|
|
180576
|
-
const readStdin = io.stdin ?? (() =>
|
|
182103
|
+
const readStdin = io.stdin ?? (() => readFileSync9(0, "utf8"));
|
|
180577
182104
|
inputFiles.push(...readStdin().split(`
|
|
180578
182105
|
`));
|
|
180579
182106
|
}
|
|
@@ -180653,8 +182180,18 @@ function runGraph(options, io) {
|
|
|
180653
182180
|
return 0;
|
|
180654
182181
|
});
|
|
180655
182182
|
}
|
|
182183
|
+
function repairGuidanceFor(diagnostics, projectRoot) {
|
|
182184
|
+
const invalid = diagnostics.filter((diagnostic) => diagnostic.code === "config_file_invalid");
|
|
182185
|
+
if (invalid.some((diagnostic) => diagnostic.target === LINK_MANIFEST_FILE_NAME)) {
|
|
182186
|
+
return manifestRepairGuidance();
|
|
182187
|
+
}
|
|
182188
|
+
if (invalid.length === 0) {
|
|
182189
|
+
return;
|
|
182190
|
+
}
|
|
182191
|
+
return existsSync6(join14(projectRoot, CONFIG_FILE_NAME)) ? configRepairGuidance() : configSetupGuidance();
|
|
182192
|
+
}
|
|
180656
182193
|
function unknownCommandGuidance(command) {
|
|
180657
|
-
const suggestion =
|
|
182194
|
+
const suggestion = nearestMatch(command, SUBCOMMANDS);
|
|
180658
182195
|
const lines = ["Available commands:", ` ${SUBCOMMANDS.join(", ")}`];
|
|
180659
182196
|
if (suggestion !== undefined) {
|
|
180660
182197
|
lines.push("", `Did you mean \`${suggestion}\`?`);
|
|
@@ -180663,49 +182200,6 @@ function unknownCommandGuidance(command) {
|
|
|
180663
182200
|
return lines.join(`
|
|
180664
182201
|
`);
|
|
180665
182202
|
}
|
|
180666
|
-
function nearestSubcommand(input) {
|
|
180667
|
-
const ranked = SUBCOMMANDS.map((command, index) => ({
|
|
180668
|
-
command,
|
|
180669
|
-
distance: editDistance(input, command),
|
|
180670
|
-
index
|
|
180671
|
-
})).toSorted((left, right) => left.distance - right.distance || left.index - right.index);
|
|
180672
|
-
const abbreviation = ranked.find(({ command }) => isOrderedAbbreviation(input, command));
|
|
180673
|
-
if (abbreviation !== undefined) {
|
|
180674
|
-
return abbreviation.command;
|
|
180675
|
-
}
|
|
180676
|
-
const best = ranked[0];
|
|
180677
|
-
if (best === undefined) {
|
|
180678
|
-
return;
|
|
180679
|
-
}
|
|
180680
|
-
const closeEnough = best.distance <= Math.max(1, Math.floor(best.command.length / 2));
|
|
180681
|
-
return closeEnough ? best.command : undefined;
|
|
180682
|
-
}
|
|
180683
|
-
function isOrderedAbbreviation(input, command) {
|
|
180684
|
-
if (input.length < 3 || input.length >= command.length) {
|
|
180685
|
-
return false;
|
|
180686
|
-
}
|
|
180687
|
-
let commandIndex = 0;
|
|
180688
|
-
for (const character of input) {
|
|
180689
|
-
const matchIndex = command.indexOf(character, commandIndex);
|
|
180690
|
-
if (matchIndex === -1) {
|
|
180691
|
-
return false;
|
|
180692
|
-
}
|
|
180693
|
-
commandIndex = matchIndex + 1;
|
|
180694
|
-
}
|
|
180695
|
-
return true;
|
|
180696
|
-
}
|
|
180697
|
-
function editDistance(left, right) {
|
|
180698
|
-
let previous = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
180699
|
-
for (let leftIndex = 1;leftIndex <= left.length; leftIndex += 1) {
|
|
180700
|
-
const current = [leftIndex];
|
|
180701
|
-
for (let rightIndex = 1;rightIndex <= right.length; rightIndex += 1) {
|
|
180702
|
-
const substitutionCost = left[leftIndex - 1] === right[rightIndex - 1] ? 0 : 1;
|
|
180703
|
-
current[rightIndex] = Math.min((current[rightIndex - 1] ?? Number.POSITIVE_INFINITY) + 1, (previous[rightIndex] ?? Number.POSITIVE_INFINITY) + 1, (previous[rightIndex - 1] ?? Number.POSITIVE_INFINITY) + substitutionCost);
|
|
180704
|
-
}
|
|
180705
|
-
previous = current;
|
|
180706
|
-
}
|
|
180707
|
-
return previous[right.length] ?? 0;
|
|
180708
|
-
}
|
|
180709
182203
|
var COMMAND_HANDLERS = {
|
|
180710
182204
|
check: (args, io) => runCheck(parseCheckOptions(args), io),
|
|
180711
182205
|
related: (args, io) => runRelated(parseRelatedOptions(args), io),
|
|
@@ -180714,6 +182208,12 @@ var COMMAND_HANDLERS = {
|
|
|
180714
182208
|
docs: (args, io) => runDocs(parseDocsCommand(args), io),
|
|
180715
182209
|
init: (args, io, initRuntime) => runInit(parseInitOptions(args, "init"), io, initRuntime),
|
|
180716
182210
|
"init-with-agent": (args, io, initRuntime) => runInitWithAgent(parseInitOptions(args, "init-with-agent"), io, initRuntime),
|
|
182211
|
+
upgrade: (args, io, initRuntime, cliRuntime) => runUpgrade(parseUpgradeOptions(args), io, initRuntime, {
|
|
182212
|
+
latest: cliRuntime.latest ?? { status: "unavailable", source: "network" },
|
|
182213
|
+
currentVersion: VERSION,
|
|
182214
|
+
...cliRuntime.env !== undefined ? { env: cliRuntime.env } : {},
|
|
182215
|
+
...cliRuntime.currentDirectory !== undefined ? { currentDirectory: cliRuntime.currentDirectory } : {}
|
|
182216
|
+
}),
|
|
180717
182217
|
lsp: (args) => {
|
|
180718
182218
|
if (args.length > 0) {
|
|
180719
182219
|
throw new CliError("lsp takes no options.", commandHelpGuidance("lsp"));
|
|
@@ -180725,11 +182225,12 @@ var COMMAND_HANDLERS = {
|
|
|
180725
182225
|
function run(argv, io = {
|
|
180726
182226
|
stdout: (text) => process.stdout.write(text),
|
|
180727
182227
|
stderr: (text) => process.stderr.write(text)
|
|
180728
|
-
}, initRuntime = { prompts: createDefaultPrompts() }) {
|
|
182228
|
+
}, initRuntime = { prompts: createDefaultPrompts() }, cliRuntime = {}) {
|
|
180729
182229
|
const [command, ...rest] = argv;
|
|
180730
182230
|
try {
|
|
180731
182231
|
if (command === undefined || command === "--help" || command === "-h") {
|
|
180732
182232
|
io.stdout(GLOBAL_HELP);
|
|
182233
|
+
writeUpdateNotice(argv, io, initRuntime, cliRuntime);
|
|
180733
182234
|
return 0;
|
|
180734
182235
|
}
|
|
180735
182236
|
if (isSubcommand(command) && hasHelpFlag(rest)) {
|
|
@@ -180739,10 +182240,13 @@ function run(argv, io = {
|
|
|
180739
182240
|
if (command === "--version" || command === "-v") {
|
|
180740
182241
|
io.stdout(`${VERSION}
|
|
180741
182242
|
`);
|
|
182243
|
+
writeUpdateNotice(argv, io, initRuntime, cliRuntime);
|
|
180742
182244
|
return 0;
|
|
180743
182245
|
}
|
|
180744
182246
|
if (isSubcommand(command)) {
|
|
180745
|
-
|
|
182247
|
+
const exitCode = COMMAND_HANDLERS[command](rest, io, initRuntime, cliRuntime);
|
|
182248
|
+
writeUpdateNotice(argv, io, initRuntime, cliRuntime);
|
|
182249
|
+
return exitCode;
|
|
180746
182250
|
}
|
|
180747
182251
|
throw new CliError(`Unknown command: ${command}`, unknownCommandGuidance(command));
|
|
180748
182252
|
} catch (error) {
|
|
@@ -180756,8 +182260,59 @@ function run(argv, io = {
|
|
|
180756
182260
|
return 1;
|
|
180757
182261
|
}
|
|
180758
182262
|
}
|
|
182263
|
+
function rootOptionOf(argv, currentDirectory) {
|
|
182264
|
+
const flagIndex = argv.indexOf("--root");
|
|
182265
|
+
const value = flagIndex === -1 ? undefined : argv[flagIndex + 1];
|
|
182266
|
+
return value === undefined ? currentDirectory : resolve5(currentDirectory, value);
|
|
182267
|
+
}
|
|
182268
|
+
function writeUpdateNotice(argv, io, initRuntime, cliRuntime) {
|
|
182269
|
+
const latest = cliRuntime.latest;
|
|
182270
|
+
if (latest === undefined || latest.status !== "ok") {
|
|
182271
|
+
return;
|
|
182272
|
+
}
|
|
182273
|
+
if (!decideUpdateCheck({
|
|
182274
|
+
argv,
|
|
182275
|
+
...cliRuntime.env !== undefined ? { env: cliRuntime.env } : {},
|
|
182276
|
+
isTty: cliRuntime.isTty ?? false
|
|
182277
|
+
}).enabled) {
|
|
182278
|
+
return;
|
|
182279
|
+
}
|
|
182280
|
+
const packageRoot = initRuntime.packageRoot ?? resolvePackageRoot();
|
|
182281
|
+
const currentDirectory = cliRuntime.currentDirectory ?? process.cwd();
|
|
182282
|
+
const notice = formatUpdateNotice({
|
|
182283
|
+
current: VERSION,
|
|
182284
|
+
latest: latest.latest,
|
|
182285
|
+
guidance: detectUpgradeGuidance({
|
|
182286
|
+
packageRoot,
|
|
182287
|
+
projectRoot: rootOptionOf(argv, currentDirectory),
|
|
182288
|
+
currentDirectory,
|
|
182289
|
+
...cliRuntime.env !== undefined ? { env: cliRuntime.env } : {}
|
|
182290
|
+
})
|
|
182291
|
+
});
|
|
182292
|
+
if (notice !== undefined) {
|
|
182293
|
+
io.stderr(notice);
|
|
182294
|
+
}
|
|
182295
|
+
}
|
|
182296
|
+
async function resolveLatestForInvocation(argv, env, isTty) {
|
|
182297
|
+
if (isUpdateCheckOptedOut(env)) {
|
|
182298
|
+
return;
|
|
182299
|
+
}
|
|
182300
|
+
if (argv[0] === "upgrade" && !hasHelpFlag(argv.slice(1))) {
|
|
182301
|
+
return resolveLatestStableVersion({ forceRefresh: true });
|
|
182302
|
+
}
|
|
182303
|
+
if (!decideUpdateCheck({ argv, env, isTty }).enabled) {
|
|
182304
|
+
return;
|
|
182305
|
+
}
|
|
182306
|
+
return resolveLatestStableVersion();
|
|
182307
|
+
}
|
|
180759
182308
|
if (__require.main == __require.module) {
|
|
180760
|
-
|
|
182309
|
+
const argv = process.argv.slice(2);
|
|
182310
|
+
const isTty = process.stderr.isTTY === true;
|
|
182311
|
+
const latest = await resolveLatestForInvocation(argv, process.env, isTty);
|
|
182312
|
+
process.exitCode = run(argv, {
|
|
182313
|
+
stdout: (text) => process.stdout.write(text),
|
|
182314
|
+
stderr: (text) => process.stderr.write(text)
|
|
182315
|
+
}, { prompts: createDefaultPrompts() }, { ...latest !== undefined ? { latest } : {}, env: process.env, isTty });
|
|
180761
182316
|
}
|
|
180762
182317
|
export {
|
|
180763
182318
|
run,
|