docbridge 0.8.0 → 0.10.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 +85 -1
- package/README.md +55 -369
- 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 +2 -2
- package/dist/bin/linux-x64/docbridge_dart_scanner +0 -0
- package/dist/index.js +2597 -1119
- package/docs/README.md +67 -0
- package/docs/user/automation.md +104 -0
- package/docs/user/commands.md +67 -4
- package/docs/user/configuration.md +5 -4
- package/docs/user/getting-started.md +4 -4
- package/docs/user/linking.md +202 -0
- package/docs/user/troubleshooting.md +50 -18
- package/package.json +1 -8
- package/schemas/docbridge.links.schema.json +39 -0
- package/schemas/scanner-worker.schema.json +1 -0
- package/templates/skills/docbridge/SKILL.md +12 -13
- package/docs/user/agent-integration.md +0 -96
- package/docs/user/annotations.md +0 -173
- package/docs/user/link-review.md +0 -53
- package/docs/user/linking-workflow.md +0 -70
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.10.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",
|
|
@@ -175614,13 +175614,6 @@ var package_default = {
|
|
|
175614
175614
|
"LICENSE"
|
|
175615
175615
|
],
|
|
175616
175616
|
type: "module",
|
|
175617
|
-
scripts: {
|
|
175618
|
-
docbridge: "bun run src/cli/index.ts",
|
|
175619
|
-
check: "bun run src/cli/index.ts check",
|
|
175620
|
-
"check:example": "bun run src/cli/index.ts check --root examples/typescript",
|
|
175621
|
-
test: "bun test",
|
|
175622
|
-
build: "bun build src/cli/index.ts --outdir dist --target node"
|
|
175623
|
-
},
|
|
175624
175617
|
devDependencies: {
|
|
175625
175618
|
"@types/bun": "^1.3.14",
|
|
175626
175619
|
ajv: "^8.20.0",
|
|
@@ -175636,20 +175629,7 @@ var package_default = {
|
|
|
175636
175629
|
|
|
175637
175630
|
// src/core/config.ts
|
|
175638
175631
|
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
|
-
}
|
|
175632
|
+
import { join as join2 } from "node:path";
|
|
175653
175633
|
|
|
175654
175634
|
// src/core/glob.ts
|
|
175655
175635
|
import { lstatSync, readFileSync, readdirSync } from "node:fs";
|
|
@@ -175802,12 +175782,562 @@ function escapeRegExpChar(char) {
|
|
|
175802
175782
|
return /[.*+?^${}()|[\]\\]/.test(char) ? `\\${char}` : char;
|
|
175803
175783
|
}
|
|
175804
175784
|
|
|
175785
|
+
// src/core/code-language.ts
|
|
175786
|
+
var KNOWN_CODE_LANGUAGES = [
|
|
175787
|
+
"typescript",
|
|
175788
|
+
"swift",
|
|
175789
|
+
"dart",
|
|
175790
|
+
"rust"
|
|
175791
|
+
];
|
|
175792
|
+
function isCodeLanguage(value) {
|
|
175793
|
+
return KNOWN_CODE_LANGUAGES.includes(value);
|
|
175794
|
+
}
|
|
175795
|
+
function collectCodeFiles(projectRoot, codeInclude) {
|
|
175796
|
+
const collected = [];
|
|
175797
|
+
const seen = new Set;
|
|
175798
|
+
for (const language of KNOWN_CODE_LANGUAGES) {
|
|
175799
|
+
const entry = codeInclude[language];
|
|
175800
|
+
if (entry === undefined) {
|
|
175801
|
+
continue;
|
|
175802
|
+
}
|
|
175803
|
+
for (const relPath of collectFiles(projectRoot, entry.patterns)) {
|
|
175804
|
+
if (seen.has(relPath)) {
|
|
175805
|
+
continue;
|
|
175806
|
+
}
|
|
175807
|
+
seen.add(relPath);
|
|
175808
|
+
collected.push({ language, relPath });
|
|
175809
|
+
}
|
|
175810
|
+
}
|
|
175811
|
+
collected.sort((left, right) => comparePaths(left.relPath, right.relPath));
|
|
175812
|
+
return collected;
|
|
175813
|
+
}
|
|
175814
|
+
function codeFileOwners(projectRoot, codeInclude) {
|
|
175815
|
+
const owners = new Map;
|
|
175816
|
+
for (const language of KNOWN_CODE_LANGUAGES) {
|
|
175817
|
+
const entry = codeInclude[language];
|
|
175818
|
+
if (entry === undefined) {
|
|
175819
|
+
continue;
|
|
175820
|
+
}
|
|
175821
|
+
for (const relPath of collectFiles(projectRoot, entry.patterns)) {
|
|
175822
|
+
const existing = owners.get(relPath);
|
|
175823
|
+
if (existing === undefined) {
|
|
175824
|
+
owners.set(relPath, [language]);
|
|
175825
|
+
} else if (!existing.includes(language)) {
|
|
175826
|
+
existing.push(language);
|
|
175827
|
+
}
|
|
175828
|
+
}
|
|
175829
|
+
}
|
|
175830
|
+
return owners;
|
|
175831
|
+
}
|
|
175832
|
+
|
|
175833
|
+
// src/core/config.ts
|
|
175834
|
+
var CONFIG_FILE_NAME = "docbridge.config.json";
|
|
175835
|
+
var EMPTY_CONFIG = {
|
|
175836
|
+
include: { code: {}, docs: [] }
|
|
175837
|
+
};
|
|
175838
|
+
var KNOWN_TOP_LEVEL_KEYS = new Set(["$schema", "include"]);
|
|
175839
|
+
var KNOWN_INCLUDE_KEYS = new Set(["code", "docs"]);
|
|
175840
|
+
var KNOWN_CODE_ENTRY_KEYS = new Set(["patterns", "visibility"]);
|
|
175841
|
+
var LANGUAGE_SUFFIX = {
|
|
175842
|
+
typescript: ".ts",
|
|
175843
|
+
swift: ".swift",
|
|
175844
|
+
dart: ".dart",
|
|
175845
|
+
rust: ".rs"
|
|
175846
|
+
};
|
|
175847
|
+
var LANGUAGE_VISIBILITY = {
|
|
175848
|
+
typescript: ["public", "protected", "private"],
|
|
175849
|
+
swift: ["public", "open", "internal"],
|
|
175850
|
+
dart: ["public"],
|
|
175851
|
+
rust: ["pub", "private"]
|
|
175852
|
+
};
|
|
175853
|
+
function loadConfig(projectRoot) {
|
|
175854
|
+
let rawText;
|
|
175855
|
+
try {
|
|
175856
|
+
rawText = readFileSync2(join2(projectRoot, CONFIG_FILE_NAME), "utf8");
|
|
175857
|
+
} catch {
|
|
175858
|
+
rawText = undefined;
|
|
175859
|
+
}
|
|
175860
|
+
const resolved = resolveConfig(rawText);
|
|
175861
|
+
if (!resolved.ok) {
|
|
175862
|
+
return resolved;
|
|
175863
|
+
}
|
|
175864
|
+
const overlapDiagnostics = detectLanguageOverlap(projectRoot, resolved.config.include.code);
|
|
175865
|
+
if (overlapDiagnostics.length === 0) {
|
|
175866
|
+
return resolved;
|
|
175867
|
+
}
|
|
175868
|
+
return {
|
|
175869
|
+
config: EMPTY_CONFIG,
|
|
175870
|
+
diagnostics: [...resolved.diagnostics, ...overlapDiagnostics],
|
|
175871
|
+
ok: false
|
|
175872
|
+
};
|
|
175873
|
+
}
|
|
175874
|
+
function resolveConfig(rawText) {
|
|
175875
|
+
if (rawText === undefined) {
|
|
175876
|
+
return {
|
|
175877
|
+
config: EMPTY_CONFIG,
|
|
175878
|
+
ok: false,
|
|
175879
|
+
diagnostics: [
|
|
175880
|
+
configDiagnostic("config_file_invalid", CONFIG_FILE_NAME, `${CONFIG_FILE_NAME} was not found. DocBridge requires a configuration file.`)
|
|
175881
|
+
]
|
|
175882
|
+
};
|
|
175883
|
+
}
|
|
175884
|
+
let parsed;
|
|
175885
|
+
try {
|
|
175886
|
+
parsed = JSON.parse(rawText);
|
|
175887
|
+
} catch (error) {
|
|
175888
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
175889
|
+
return {
|
|
175890
|
+
config: EMPTY_CONFIG,
|
|
175891
|
+
ok: false,
|
|
175892
|
+
diagnostics: [
|
|
175893
|
+
configDiagnostic("config_file_invalid", CONFIG_FILE_NAME, `Failed to parse ${CONFIG_FILE_NAME}: ${reason}`)
|
|
175894
|
+
]
|
|
175895
|
+
};
|
|
175896
|
+
}
|
|
175897
|
+
const diagnostics = [];
|
|
175898
|
+
if (!isPlainObject(parsed)) {
|
|
175899
|
+
diagnostics.push(configDiagnostic("config_invalid_value", CONFIG_FILE_NAME, "Configuration must be a JSON object."));
|
|
175900
|
+
return { config: EMPTY_CONFIG, diagnostics, ok: false };
|
|
175901
|
+
}
|
|
175902
|
+
for (const key of Object.keys(parsed)) {
|
|
175903
|
+
if (!KNOWN_TOP_LEVEL_KEYS.has(key)) {
|
|
175904
|
+
diagnostics.push(configDiagnostic("config_unknown_key", key, `Unknown top-level configuration key: ${key}`));
|
|
175905
|
+
}
|
|
175906
|
+
}
|
|
175907
|
+
if ("$schema" in parsed && typeof parsed.$schema !== "string") {
|
|
175908
|
+
diagnostics.push(configDiagnostic("config_invalid_value", "$schema", "`$schema` must be a string."));
|
|
175909
|
+
}
|
|
175910
|
+
const include = parsed.include;
|
|
175911
|
+
if (!isPlainObject(include)) {
|
|
175912
|
+
diagnostics.push(configDiagnostic("config_invalid_value", "include", "`include` must be an object with `code` and `docs`."));
|
|
175913
|
+
return { config: EMPTY_CONFIG, diagnostics, ok: false };
|
|
175914
|
+
}
|
|
175915
|
+
for (const key of Object.keys(include)) {
|
|
175916
|
+
if (!KNOWN_INCLUDE_KEYS.has(key)) {
|
|
175917
|
+
diagnostics.push(configDiagnostic("config_unknown_key", `include.${key}`, `Unknown configuration key under \`include\`: ${key}`));
|
|
175918
|
+
}
|
|
175919
|
+
}
|
|
175920
|
+
const code = validateCodeInclude(include.code, diagnostics);
|
|
175921
|
+
validatePatternArray(include.docs, "docs", ".md", false, diagnostics);
|
|
175922
|
+
const ok = diagnostics.length === 0;
|
|
175923
|
+
const config = ok ? { include: { code, docs: include.docs } } : EMPTY_CONFIG;
|
|
175924
|
+
return { config, diagnostics, ok };
|
|
175925
|
+
}
|
|
175926
|
+
function validateCodeInclude(value, diagnostics) {
|
|
175927
|
+
if (Array.isArray(value)) {
|
|
175928
|
+
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.'));
|
|
175929
|
+
return {};
|
|
175930
|
+
}
|
|
175931
|
+
if (!isPlainObject(value)) {
|
|
175932
|
+
diagnostics.push(configDiagnostic("config_invalid_value", "include.code", "`include.code` must be a language-keyed object."));
|
|
175933
|
+
return {};
|
|
175934
|
+
}
|
|
175935
|
+
const keys = Object.keys(value);
|
|
175936
|
+
if (keys.length === 0) {
|
|
175937
|
+
diagnostics.push(configDiagnostic("config_invalid_value", "include.code", "`include.code` must configure at least one language."));
|
|
175938
|
+
return {};
|
|
175939
|
+
}
|
|
175940
|
+
const result = {};
|
|
175941
|
+
for (const key of keys) {
|
|
175942
|
+
if (!isCodeLanguage(key)) {
|
|
175943
|
+
diagnostics.push(configDiagnostic("config_invalid_value", `include.code.${key}`, `Unknown code language: ${key}. Supported languages: ${KNOWN_CODE_LANGUAGES.join(", ")}.`));
|
|
175944
|
+
continue;
|
|
175945
|
+
}
|
|
175946
|
+
const entry = validateCodeEntry(key, value[key], diagnostics);
|
|
175947
|
+
if (entry !== undefined) {
|
|
175948
|
+
result[key] = entry;
|
|
175949
|
+
}
|
|
175950
|
+
}
|
|
175951
|
+
return result;
|
|
175952
|
+
}
|
|
175953
|
+
function validateCodeEntry(language, value, diagnostics) {
|
|
175954
|
+
const target = `include.code.${language}`;
|
|
175955
|
+
if (!isPlainObject(value)) {
|
|
175956
|
+
diagnostics.push(configDiagnostic("config_invalid_value", target, `\`${target}\` must be an object with a \`patterns\` array. Shorthand arrays are not supported.`));
|
|
175957
|
+
return;
|
|
175958
|
+
}
|
|
175959
|
+
for (const key of Object.keys(value)) {
|
|
175960
|
+
if (!KNOWN_CODE_ENTRY_KEYS.has(key)) {
|
|
175961
|
+
diagnostics.push(configDiagnostic("config_unknown_key", `${target}.${key}`, `Unknown configuration key under \`${target}\`: ${key}`));
|
|
175962
|
+
}
|
|
175963
|
+
}
|
|
175964
|
+
const before = diagnostics.length;
|
|
175965
|
+
validatePatternArray(value.patterns, `code.${language}.patterns`, LANGUAGE_SUFFIX[language], language === "typescript", diagnostics);
|
|
175966
|
+
if ("visibility" in value) {
|
|
175967
|
+
if (!isStringArray(value.visibility)) {
|
|
175968
|
+
diagnostics.push(configDiagnostic("config_invalid_value", `${target}.visibility`, `\`${target}.visibility\` must be an array of strings.`));
|
|
175969
|
+
} else {
|
|
175970
|
+
validateVisibilityOptions(language, value.visibility, target, diagnostics);
|
|
175971
|
+
}
|
|
175972
|
+
}
|
|
175973
|
+
if (diagnostics.length !== before) {
|
|
175974
|
+
return;
|
|
175975
|
+
}
|
|
175976
|
+
const entry = { patterns: value.patterns };
|
|
175977
|
+
if (isStringArray(value.visibility)) {
|
|
175978
|
+
entry.visibility = value.visibility;
|
|
175979
|
+
}
|
|
175980
|
+
return entry;
|
|
175981
|
+
}
|
|
175982
|
+
function validateVisibilityOptions(language, values, target, diagnostics) {
|
|
175983
|
+
const allowed = LANGUAGE_VISIBILITY[language];
|
|
175984
|
+
if (allowed.length === 0) {
|
|
175985
|
+
diagnostics.push(configDiagnostic("config_invalid_value", `${target}.visibility`, `\`${target}.visibility\` is not supported for ${language}.`));
|
|
175986
|
+
return;
|
|
175987
|
+
}
|
|
175988
|
+
for (const value of values) {
|
|
175989
|
+
if (!allowed.includes(value)) {
|
|
175990
|
+
diagnostics.push(configDiagnostic("config_invalid_value", `${target}.visibility`, `Unsupported ${language} visibility: ${value}. Supported values: ${allowed.join(", ")}.`));
|
|
175991
|
+
}
|
|
175992
|
+
}
|
|
175993
|
+
}
|
|
175994
|
+
function validatePatternArray(value, field, requiredSuffix, excludeDeclarationFiles, diagnostics) {
|
|
175995
|
+
const target = `include.${field}`;
|
|
175996
|
+
if (!Array.isArray(value)) {
|
|
175997
|
+
diagnostics.push(configDiagnostic("config_invalid_value", target, `\`${target}\` must be a non-empty array of strings.`));
|
|
175998
|
+
return;
|
|
175999
|
+
}
|
|
176000
|
+
if (value.length === 0) {
|
|
176001
|
+
diagnostics.push(configDiagnostic("config_invalid_value", target, `\`${target}\` must contain at least one pattern.`));
|
|
176002
|
+
return;
|
|
176003
|
+
}
|
|
176004
|
+
for (const pattern of value) {
|
|
176005
|
+
if (typeof pattern !== "string") {
|
|
176006
|
+
diagnostics.push(configDiagnostic("config_invalid_value", target, `\`${target}\` patterns must be strings.`));
|
|
176007
|
+
continue;
|
|
176008
|
+
}
|
|
176009
|
+
const globResult = validateGlobPattern(pattern);
|
|
176010
|
+
if (!globResult.ok) {
|
|
176011
|
+
diagnostics.push(configDiagnostic("config_invalid_value", pattern, `Invalid pattern in \`${target}\`: ${globResult.message}`));
|
|
176012
|
+
continue;
|
|
176013
|
+
}
|
|
176014
|
+
const suffixError = checkSuffix(pattern, requiredSuffix, excludeDeclarationFiles);
|
|
176015
|
+
if (suffixError !== undefined) {
|
|
176016
|
+
diagnostics.push(configDiagnostic("config_invalid_value", pattern, suffixError));
|
|
176017
|
+
}
|
|
176018
|
+
}
|
|
176019
|
+
}
|
|
176020
|
+
function checkSuffix(pattern, requiredSuffix, excludeDeclarationFiles) {
|
|
176021
|
+
if (!pattern.endsWith(requiredSuffix)) {
|
|
176022
|
+
return `Pattern must end with \`${requiredSuffix}\`.`;
|
|
176023
|
+
}
|
|
176024
|
+
if (excludeDeclarationFiles && pattern.endsWith(".d.ts")) {
|
|
176025
|
+
return "Pattern must not target `.d.ts` declaration files.";
|
|
176026
|
+
}
|
|
176027
|
+
return;
|
|
176028
|
+
}
|
|
176029
|
+
function detectLanguageOverlap(projectRoot, code) {
|
|
176030
|
+
const diagnostics = [];
|
|
176031
|
+
for (const [relPath, owners] of codeFileOwners(projectRoot, code)) {
|
|
176032
|
+
if (owners.length > 1) {
|
|
176033
|
+
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.`));
|
|
176034
|
+
}
|
|
176035
|
+
}
|
|
176036
|
+
return diagnostics;
|
|
176037
|
+
}
|
|
176038
|
+
function configDiagnostic(code, target, message) {
|
|
176039
|
+
return { severity: "error", code, target, message };
|
|
176040
|
+
}
|
|
176041
|
+
function isPlainObject(value) {
|
|
176042
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
176043
|
+
}
|
|
176044
|
+
function isStringArray(value) {
|
|
176045
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
176046
|
+
}
|
|
176047
|
+
|
|
176048
|
+
// src/core/diagnostics.ts
|
|
176049
|
+
function sortDiagnostics(diagnostics) {
|
|
176050
|
+
return [...diagnostics].toSorted(compareDiagnostics);
|
|
176051
|
+
}
|
|
176052
|
+
function summarizeDiagnostics(diagnostics) {
|
|
176053
|
+
const summary = { errors: 0, warnings: 0 };
|
|
176054
|
+
for (const diagnostic of diagnostics) {
|
|
176055
|
+
if (diagnostic.severity === "error") {
|
|
176056
|
+
summary.errors += 1;
|
|
176057
|
+
} else {
|
|
176058
|
+
summary.warnings += 1;
|
|
176059
|
+
}
|
|
176060
|
+
}
|
|
176061
|
+
return summary;
|
|
176062
|
+
}
|
|
176063
|
+
function formatDiagnostic(diagnostic) {
|
|
176064
|
+
const location = diagnostic.location;
|
|
176065
|
+
if (location === undefined) {
|
|
176066
|
+
return `${diagnostic.target} ${diagnostic.severity} ${diagnostic.code} - ${diagnostic.message}`;
|
|
176067
|
+
}
|
|
176068
|
+
const prefix = `${location.filePath}:${location.line}:${location.column}`;
|
|
176069
|
+
return `${prefix} ${diagnostic.severity} ${diagnostic.code} ${diagnostic.target} - ${diagnostic.message}`;
|
|
176070
|
+
}
|
|
176071
|
+
function formatSummary(summary) {
|
|
176072
|
+
return `Summary: ${summary.errors} ${pluralize("error", summary.errors)}, ${summary.warnings} ${pluralize("warning", summary.warnings)}`;
|
|
176073
|
+
}
|
|
176074
|
+
function collectErroredFiles(diagnostics) {
|
|
176075
|
+
const errored = new Set;
|
|
176076
|
+
for (const diagnostic of diagnostics) {
|
|
176077
|
+
if (diagnostic.code === "file_read_error" || diagnostic.code === "code_parse_error" || isFileScopedScannerDiagnostic(diagnostic)) {
|
|
176078
|
+
errored.add(diagnostic.target);
|
|
176079
|
+
}
|
|
176080
|
+
}
|
|
176081
|
+
return errored;
|
|
176082
|
+
}
|
|
176083
|
+
function isFileScopedScannerDiagnostic(diagnostic) {
|
|
176084
|
+
return (diagnostic.code === "code_scanner_unavailable" || diagnostic.code === "code_scanner_failed") && diagnostic.language !== undefined && diagnostic.target !== diagnostic.language;
|
|
176085
|
+
}
|
|
176086
|
+
function compareDiagnostics(left, right) {
|
|
176087
|
+
const leftLocation = left.location;
|
|
176088
|
+
const rightLocation = right.location;
|
|
176089
|
+
if (leftLocation === undefined && rightLocation !== undefined) {
|
|
176090
|
+
return -1;
|
|
176091
|
+
}
|
|
176092
|
+
if (leftLocation !== undefined && rightLocation === undefined) {
|
|
176093
|
+
return 1;
|
|
176094
|
+
}
|
|
176095
|
+
if (leftLocation !== undefined && rightLocation !== undefined) {
|
|
176096
|
+
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);
|
|
176097
|
+
}
|
|
176098
|
+
return compareString(left.code, right.code) || compareString(left.target, right.target);
|
|
176099
|
+
}
|
|
176100
|
+
function compareString(left, right) {
|
|
176101
|
+
return left.localeCompare(right);
|
|
176102
|
+
}
|
|
176103
|
+
function compareNumber(left, right) {
|
|
176104
|
+
return left - right;
|
|
176105
|
+
}
|
|
176106
|
+
function pluralize(word, count) {
|
|
176107
|
+
return count === 1 ? word : `${word}s`;
|
|
176108
|
+
}
|
|
176109
|
+
|
|
176110
|
+
// src/core/endpoint.ts
|
|
176111
|
+
function filePathOf(endpoint) {
|
|
176112
|
+
const hashIndex = endpoint.indexOf("#");
|
|
176113
|
+
return hashIndex === -1 ? endpoint : endpoint.slice(0, hashIndex);
|
|
176114
|
+
}
|
|
176115
|
+
function fragmentOf(endpoint) {
|
|
176116
|
+
const hashIndex = endpoint.indexOf("#");
|
|
176117
|
+
return hashIndex === -1 ? endpoint : endpoint.slice(hashIndex + 1);
|
|
176118
|
+
}
|
|
176119
|
+
function compareEndpointOrder(left, right) {
|
|
176120
|
+
return left.filePath.localeCompare(right.filePath) || left.line - right.line || left.column - right.column || left.endpoint.localeCompare(right.endpoint);
|
|
176121
|
+
}
|
|
176122
|
+
function endpointRange(element) {
|
|
176123
|
+
const range = element.kind === "code" ? element.nameRange : element.headingTextRange;
|
|
176124
|
+
if (range !== undefined) {
|
|
176125
|
+
return range;
|
|
176126
|
+
}
|
|
176127
|
+
const { line, column } = element.location;
|
|
176128
|
+
return { start: { line, column }, end: { line, column } };
|
|
176129
|
+
}
|
|
176130
|
+
|
|
176131
|
+
// src/core/graph.ts
|
|
176132
|
+
function buildLinkGraph(codeFiles, docFiles) {
|
|
176133
|
+
const codeByEndpoint = new Map;
|
|
176134
|
+
for (const file of codeFiles) {
|
|
176135
|
+
for (const symbol of file.symbols) {
|
|
176136
|
+
if (!codeByEndpoint.has(symbol.endpoint)) {
|
|
176137
|
+
codeByEndpoint.set(symbol.endpoint, symbol);
|
|
176138
|
+
}
|
|
176139
|
+
}
|
|
176140
|
+
}
|
|
176141
|
+
const docByEndpoint = new Map;
|
|
176142
|
+
for (const file of docFiles) {
|
|
176143
|
+
for (const anchor of file.anchors) {
|
|
176144
|
+
if (!docByEndpoint.has(anchor.endpoint)) {
|
|
176145
|
+
docByEndpoint.set(anchor.endpoint, anchor);
|
|
176146
|
+
}
|
|
176147
|
+
}
|
|
176148
|
+
}
|
|
176149
|
+
const counterparts = new Map;
|
|
176150
|
+
const addEdge = (a, b) => {
|
|
176151
|
+
addTo(counterparts, a, b);
|
|
176152
|
+
addTo(counterparts, b, a);
|
|
176153
|
+
};
|
|
176154
|
+
for (const file of codeFiles) {
|
|
176155
|
+
for (const link of file.links) {
|
|
176156
|
+
if (codeByEndpoint.has(link.source) && docByEndpoint.has(link.target)) {
|
|
176157
|
+
addEdge(link.source, link.target);
|
|
176158
|
+
}
|
|
176159
|
+
}
|
|
176160
|
+
}
|
|
176161
|
+
for (const file of docFiles) {
|
|
176162
|
+
for (const link of file.links) {
|
|
176163
|
+
if (docByEndpoint.has(link.source) && codeByEndpoint.has(link.target)) {
|
|
176164
|
+
addEdge(link.source, link.target);
|
|
176165
|
+
}
|
|
176166
|
+
}
|
|
176167
|
+
}
|
|
176168
|
+
return { codeByEndpoint, docByEndpoint, counterparts };
|
|
176169
|
+
}
|
|
176170
|
+
function counterpartsOf(graph, endpoint) {
|
|
176171
|
+
const targets = graph.counterparts.get(endpoint);
|
|
176172
|
+
if (targets === undefined) {
|
|
176173
|
+
return [];
|
|
176174
|
+
}
|
|
176175
|
+
const resolved = [];
|
|
176176
|
+
for (const target of targets) {
|
|
176177
|
+
const code = graph.codeByEndpoint.get(target);
|
|
176178
|
+
if (code !== undefined) {
|
|
176179
|
+
resolved.push(code);
|
|
176180
|
+
continue;
|
|
176181
|
+
}
|
|
176182
|
+
const doc = graph.docByEndpoint.get(target);
|
|
176183
|
+
if (doc !== undefined) {
|
|
176184
|
+
resolved.push(doc);
|
|
176185
|
+
}
|
|
176186
|
+
}
|
|
176187
|
+
return resolved.toSorted(compareEndpoints);
|
|
176188
|
+
}
|
|
176189
|
+
function addTo(map, key, value) {
|
|
176190
|
+
const existing = map.get(key);
|
|
176191
|
+
if (existing === undefined) {
|
|
176192
|
+
map.set(key, new Set([value]));
|
|
176193
|
+
return;
|
|
176194
|
+
}
|
|
176195
|
+
existing.add(value);
|
|
176196
|
+
}
|
|
176197
|
+
function compareEndpoints(left, right) {
|
|
176198
|
+
return compareEndpointOrder({ ...left.location, endpoint: left.endpoint }, { ...right.location, endpoint: right.endpoint });
|
|
176199
|
+
}
|
|
176200
|
+
|
|
176201
|
+
// src/core/code-adapter-registry.ts
|
|
176202
|
+
var codeAdapters = {};
|
|
176203
|
+
|
|
176204
|
+
// src/core/scanner-executable.ts
|
|
176205
|
+
import { accessSync, chmodSync, constants, existsSync, realpathSync, statSync } from "node:fs";
|
|
176206
|
+
import { dirname, join as join3, resolve } from "node:path";
|
|
176207
|
+
import { fileURLToPath } from "node:url";
|
|
176208
|
+
|
|
176209
|
+
// src/core/error.ts
|
|
176210
|
+
function reasonOf(error) {
|
|
176211
|
+
return error instanceof Error ? error.message : String(error);
|
|
176212
|
+
}
|
|
176213
|
+
|
|
176214
|
+
// src/core/scanner-executable.ts
|
|
176215
|
+
var SUPPORTED_SCANNER_PLATFORM_KEYS = ["darwin-arm64", "linux-x64"];
|
|
176216
|
+
var SCANNER_EXECUTABLE_NAMES = {
|
|
176217
|
+
swift: "docbridge-swift-scanner",
|
|
176218
|
+
dart: "docbridge_dart_scanner",
|
|
176219
|
+
rust: "docbridge-rust-scanner"
|
|
176220
|
+
};
|
|
176221
|
+
function scannerPlatformKey() {
|
|
176222
|
+
return `${process.platform}-${process.arch}`;
|
|
176223
|
+
}
|
|
176224
|
+
function scannerExecutableName(language) {
|
|
176225
|
+
return SCANNER_EXECUTABLE_NAMES[language];
|
|
176226
|
+
}
|
|
176227
|
+
function resolveScannerWorkerCommand(language, options = {}) {
|
|
176228
|
+
const platformKey = options.platformKey ?? scannerPlatformKey();
|
|
176229
|
+
const platformSupported = isSupportedScannerPlatformKey(platformKey);
|
|
176230
|
+
const candidates = scannerExecutableCandidates(language, platformKey, platformSupported, options);
|
|
176231
|
+
const found = candidates.find((candidate) => existsSync(candidate));
|
|
176232
|
+
if (found !== undefined) {
|
|
176233
|
+
const repair = ensureExecutable(found, options.chmod ?? chmodSync);
|
|
176234
|
+
if (!repair.ok) {
|
|
176235
|
+
return { ok: false, diagnostic: scannerUnavailableDiagnostic(language, repair.reason) };
|
|
176236
|
+
}
|
|
176237
|
+
return { ok: true, command: [found] };
|
|
176238
|
+
}
|
|
176239
|
+
if (!platformSupported) {
|
|
176240
|
+
return {
|
|
176241
|
+
ok: false,
|
|
176242
|
+
diagnostic: scannerUnavailableDiagnostic(language, `platform ${platformKey} is unsupported; supported platforms: ${SUPPORTED_SCANNER_PLATFORM_KEYS.join(", ")}`)
|
|
176243
|
+
};
|
|
176244
|
+
}
|
|
176245
|
+
return {
|
|
176246
|
+
ok: false,
|
|
176247
|
+
diagnostic: scannerUnavailableDiagnostic(language, `missing ${scannerExecutableName(language)} for platform ${platformKey}; supported platforms: ${SUPPORTED_SCANNER_PLATFORM_KEYS.join(", ")}`)
|
|
176248
|
+
};
|
|
176249
|
+
}
|
|
176250
|
+
function scannerExecutableCandidates(language, platformKey, platformSupported, options) {
|
|
176251
|
+
const sourceRoot = options.sourceRoot ?? sourceRootPath();
|
|
176252
|
+
const distRoot = options.distRoot ?? distRootPath();
|
|
176253
|
+
const executable = scannerExecutableName(language);
|
|
176254
|
+
if (language === "swift") {
|
|
176255
|
+
return [
|
|
176256
|
+
join3(sourceRoot, "packages/swift-scanner/.build/release", executable),
|
|
176257
|
+
join3(sourceRoot, "packages/swift-scanner/.build/debug", executable),
|
|
176258
|
+
...platformSupported ? [join3(distRoot, "bin", platformKey, executable)] : []
|
|
176259
|
+
];
|
|
176260
|
+
}
|
|
176261
|
+
if (language === "rust") {
|
|
176262
|
+
return [
|
|
176263
|
+
join3(sourceRoot, "packages/rust-scanner/target/release", executable),
|
|
176264
|
+
join3(sourceRoot, "packages/rust-scanner/target/debug", executable),
|
|
176265
|
+
...platformSupported ? [join3(distRoot, "bin", platformKey, executable)] : []
|
|
176266
|
+
];
|
|
176267
|
+
}
|
|
176268
|
+
return [
|
|
176269
|
+
join3(sourceRoot, "packages/dart-scanner/bin", executable),
|
|
176270
|
+
...platformSupported ? [join3(distRoot, "bin", platformKey, executable)] : []
|
|
176271
|
+
];
|
|
176272
|
+
}
|
|
176273
|
+
function ensureExecutable(path, chmod) {
|
|
176274
|
+
let mode;
|
|
176275
|
+
try {
|
|
176276
|
+
mode = statSync(path).mode;
|
|
176277
|
+
} catch (error) {
|
|
176278
|
+
return { ok: false, reason: `cannot stat ${path}: ${reasonOf(error)}` };
|
|
176279
|
+
}
|
|
176280
|
+
if (isExecutableByThisProcess(path)) {
|
|
176281
|
+
return { ok: true };
|
|
176282
|
+
}
|
|
176283
|
+
try {
|
|
176284
|
+
chmod(path, mode | 73);
|
|
176285
|
+
} catch (error) {
|
|
176286
|
+
return {
|
|
176287
|
+
ok: false,
|
|
176288
|
+
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`
|
|
176289
|
+
};
|
|
176290
|
+
}
|
|
176291
|
+
return { ok: true };
|
|
176292
|
+
}
|
|
176293
|
+
function isExecutableByThisProcess(path) {
|
|
176294
|
+
try {
|
|
176295
|
+
accessSync(path, constants.X_OK);
|
|
176296
|
+
return true;
|
|
176297
|
+
} catch {
|
|
176298
|
+
return false;
|
|
176299
|
+
}
|
|
176300
|
+
}
|
|
176301
|
+
function formatMode(mode) {
|
|
176302
|
+
return `0${(mode & 4095).toString(8).padStart(3, "0")}`;
|
|
176303
|
+
}
|
|
176304
|
+
function isSupportedScannerPlatformKey(platformKey) {
|
|
176305
|
+
return SUPPORTED_SCANNER_PLATFORM_KEYS.includes(platformKey);
|
|
176306
|
+
}
|
|
176307
|
+
function scannerRootsFromModuleUrl(moduleUrl) {
|
|
176308
|
+
const modulePath = fileURLToPath(moduleUrl);
|
|
176309
|
+
let resolved;
|
|
176310
|
+
try {
|
|
176311
|
+
resolved = realpathSync(modulePath);
|
|
176312
|
+
} catch {
|
|
176313
|
+
resolved = modulePath;
|
|
176314
|
+
}
|
|
176315
|
+
const moduleDir = dirname(resolved);
|
|
176316
|
+
return { distRoot: moduleDir, sourceRoot: resolve(moduleDir, "..", "..") };
|
|
176317
|
+
}
|
|
176318
|
+
function sourceRootPath() {
|
|
176319
|
+
return scannerRootsFromModuleUrl(import.meta.url).sourceRoot;
|
|
176320
|
+
}
|
|
176321
|
+
function distRootPath() {
|
|
176322
|
+
return scannerRootsFromModuleUrl(import.meta.url).distRoot;
|
|
176323
|
+
}
|
|
176324
|
+
function scannerUnavailableDiagnostic(language, reason) {
|
|
176325
|
+
const label = language.charAt(0).toUpperCase() + language.slice(1);
|
|
176326
|
+
return {
|
|
176327
|
+
severity: "error",
|
|
176328
|
+
code: "code_scanner_unavailable",
|
|
176329
|
+
language,
|
|
176330
|
+
target: language,
|
|
176331
|
+
message: `${label} scanner worker is unavailable: ${reason}`
|
|
176332
|
+
};
|
|
176333
|
+
}
|
|
176334
|
+
|
|
175805
176335
|
// src/core/scanner-worker.ts
|
|
175806
176336
|
var import__2020 = __toESM(require_2020(), 1);
|
|
175807
176337
|
import { spawnSync } from "node:child_process";
|
|
175808
176338
|
import { mkdirSync } from "node:fs";
|
|
175809
176339
|
import { tmpdir } from "node:os";
|
|
175810
|
-
import { dirname, join as
|
|
176340
|
+
import { dirname as dirname2, join as join4 } from "node:path";
|
|
175811
176341
|
// schemas/common-output.schema.json
|
|
175812
176342
|
var common_output_schema_default = {
|
|
175813
176343
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
@@ -175897,6 +176427,7 @@ var scanner_worker_schema_default = {
|
|
|
175897
176427
|
canonicalId: { type: "string" },
|
|
175898
176428
|
endpoint: { type: "string" },
|
|
175899
176429
|
location: { $ref: "common-output.schema.json#/$defs/location" },
|
|
176430
|
+
isMember: { type: "boolean" },
|
|
175900
176431
|
nameRange: { $ref: "common-output.schema.json#/$defs/range" },
|
|
175901
176432
|
declarationRange: { $ref: "common-output.schema.json#/$defs/range" },
|
|
175902
176433
|
signatureRange: { $ref: "common-output.schema.json#/$defs/range" }
|
|
@@ -176015,7 +176546,7 @@ function invokeScannerWorker(request, command, run = runScannerWorkerProcess) {
|
|
|
176015
176546
|
if (!processResult.ok) {
|
|
176016
176547
|
return {
|
|
176017
176548
|
ok: false,
|
|
176018
|
-
diagnostic:
|
|
176549
|
+
diagnostic: scannerUnavailableDiagnostic2(request.language, processResult.error, command[0]),
|
|
176019
176550
|
stderr: processResult.stderr
|
|
176020
176551
|
};
|
|
176021
176552
|
}
|
|
@@ -176056,7 +176587,7 @@ function invokeScannerWorker(request, command, run = runScannerWorkerProcess) {
|
|
|
176056
176587
|
}
|
|
176057
176588
|
function clangModuleCachePath() {
|
|
176058
176589
|
const owner = typeof process.getuid === "function" ? process.getuid() : "shared";
|
|
176059
|
-
return
|
|
176590
|
+
return join4(tmpdir(), `docbridge-clang-module-cache-${owner}`);
|
|
176060
176591
|
}
|
|
176061
176592
|
function runScannerWorkerProcess(input) {
|
|
176062
176593
|
try {
|
|
@@ -176132,7 +176663,7 @@ function responseFilesMatchRequest(responseFiles, requestFiles) {
|
|
|
176132
176663
|
function isRecord(value) {
|
|
176133
176664
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
176134
176665
|
}
|
|
176135
|
-
function
|
|
176666
|
+
function scannerUnavailableDiagnostic2(language, error, executable) {
|
|
176136
176667
|
const label = languageLabel(language);
|
|
176137
176668
|
return {
|
|
176138
176669
|
severity: "error",
|
|
@@ -176147,7 +176678,7 @@ function spawnFailureReason(error, executable) {
|
|
|
176147
176678
|
if (!isExecDenied(error) || executable === undefined) {
|
|
176148
176679
|
return reason;
|
|
176149
176680
|
}
|
|
176150
|
-
return `${reason}; the scanner is executable but ${
|
|
176681
|
+
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
176682
|
}
|
|
176152
176683
|
function isExecDenied(error) {
|
|
176153
176684
|
if (!(error instanceof Error)) {
|
|
@@ -176283,7 +176814,7 @@ function scanTypeScript(filePath, content, options = {}) {
|
|
|
176283
176814
|
for (const declaration of declarations) {
|
|
176284
176815
|
const endpoint = `${filePath}#${declaration.canonicalId}`;
|
|
176285
176816
|
if (!documentedEndpoints.has(endpoint)) {
|
|
176286
|
-
if (!
|
|
176817
|
+
if (!undocumentedSeen.has(endpoint)) {
|
|
176287
176818
|
undocumentedSeen.add(endpoint);
|
|
176288
176819
|
undocumentedSymbols.push(makeCodeSymbol(filePath, endpoint, declaration));
|
|
176289
176820
|
}
|
|
@@ -176609,6 +177140,9 @@ function makeCodeSymbol(filePath, endpoint, declaration) {
|
|
|
176609
177140
|
endpoint,
|
|
176610
177141
|
location: declaration.location
|
|
176611
177142
|
};
|
|
177143
|
+
if (declaration.isMember) {
|
|
177144
|
+
symbol.isMember = true;
|
|
177145
|
+
}
|
|
176612
177146
|
if (declaration.nameRange !== undefined) {
|
|
176613
177147
|
symbol.nameRange = declaration.nameRange;
|
|
176614
177148
|
}
|
|
@@ -176693,54 +177227,13 @@ function duplicateLinkDiagnostic(source, target, location, range) {
|
|
|
176693
177227
|
return diagnostic;
|
|
176694
177228
|
}
|
|
176695
177229
|
|
|
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
|
-
}
|
|
177230
|
+
// src/core/code-scan.ts
|
|
176706
177231
|
Object.assign(codeAdapters, {
|
|
176707
177232
|
typescript: typeScriptAdapter,
|
|
176708
177233
|
swift: createScannerWorkerAdapter("swift", (_projectRoot) => resolveScannerWorkerCommand("swift")),
|
|
176709
177234
|
dart: createScannerWorkerAdapter("dart", (_projectRoot) => resolveScannerWorkerCommand("dart")),
|
|
176710
177235
|
rust: createScannerWorkerAdapter("rust", (_projectRoot) => resolveScannerWorkerCommand("rust"))
|
|
176711
177236
|
});
|
|
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
177237
|
function getCodeAdapter(language) {
|
|
176745
177238
|
return codeAdapters[language];
|
|
176746
177239
|
}
|
|
@@ -176774,25 +177267,6 @@ function createScannerWorkerAdapter(language, command, adapterOptions = {}) {
|
|
|
176774
177267
|
}
|
|
176775
177268
|
};
|
|
176776
177269
|
}
|
|
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
177270
|
function scanCodeFiles(projectRoot, files, codeInclude, read, onContent) {
|
|
176797
177271
|
const codeFiles = [];
|
|
176798
177272
|
const diagnostics = [];
|
|
@@ -176817,24 +177291,6 @@ function scanCodeFiles(projectRoot, files, codeInclude, read, onContent) {
|
|
|
176817
177291
|
}
|
|
176818
177292
|
return { codeFiles, diagnostics };
|
|
176819
177293
|
}
|
|
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
177294
|
function emptyScan(language, filePath) {
|
|
176839
177295
|
return {
|
|
176840
177296
|
language,
|
|
@@ -176851,448 +177307,717 @@ function fileScopedScannerDiagnostic(diagnostic, filePath) {
|
|
|
176851
177307
|
function normalizeScannerCommand(value) {
|
|
176852
177308
|
return Array.isArray(value) ? { ok: true, command: value } : value;
|
|
176853
177309
|
}
|
|
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
|
-
];
|
|
177310
|
+
|
|
177311
|
+
// src/core/link-manifest.ts
|
|
177312
|
+
import { readFileSync as readFileSync3 } from "node:fs";
|
|
177313
|
+
import { join as join5 } from "node:path";
|
|
177314
|
+
|
|
177315
|
+
// src/core/json-source.ts
|
|
177316
|
+
class JsonSyntaxError extends Error {
|
|
177317
|
+
position;
|
|
177318
|
+
constructor(message, position) {
|
|
177319
|
+
super(message);
|
|
177320
|
+
this.position = position;
|
|
176871
177321
|
}
|
|
176872
|
-
return [
|
|
176873
|
-
join3(sourceRoot, "packages/dart-scanner/bin", executable),
|
|
176874
|
-
...platformSupported ? [join3(distRoot, "bin", platformKey, executable)] : []
|
|
176875
|
-
];
|
|
176876
177322
|
}
|
|
176877
|
-
|
|
176878
|
-
|
|
177323
|
+
var ESCAPE_VALUES = {
|
|
177324
|
+
'"': '"',
|
|
177325
|
+
"\\": "\\",
|
|
177326
|
+
"/": "/",
|
|
177327
|
+
b: "\b",
|
|
177328
|
+
f: "\f",
|
|
177329
|
+
n: `
|
|
177330
|
+
`,
|
|
177331
|
+
r: "\r",
|
|
177332
|
+
t: "\t"
|
|
177333
|
+
};
|
|
177334
|
+
function parseJsonSource(text) {
|
|
177335
|
+
const reader = new JsonReader(text);
|
|
176879
177336
|
try {
|
|
176880
|
-
|
|
177337
|
+
reader.skipWhitespace();
|
|
177338
|
+
const root = reader.readValue();
|
|
177339
|
+
reader.skipWhitespace();
|
|
177340
|
+
if (!reader.atEnd()) {
|
|
177341
|
+
reader.fail("Unexpected trailing content after the JSON value.");
|
|
177342
|
+
}
|
|
177343
|
+
return { ok: true, root };
|
|
176881
177344
|
} catch (error) {
|
|
176882
|
-
|
|
177345
|
+
if (error instanceof JsonSyntaxError) {
|
|
177346
|
+
return { ok: false, message: error.message, position: error.position };
|
|
177347
|
+
}
|
|
177348
|
+
throw error;
|
|
176883
177349
|
}
|
|
176884
|
-
|
|
176885
|
-
|
|
177350
|
+
}
|
|
177351
|
+
|
|
177352
|
+
class JsonReader {
|
|
177353
|
+
text;
|
|
177354
|
+
index = 0;
|
|
177355
|
+
line = 1;
|
|
177356
|
+
column = 1;
|
|
177357
|
+
constructor(text) {
|
|
177358
|
+
this.text = text;
|
|
177359
|
+
}
|
|
177360
|
+
atEnd() {
|
|
177361
|
+
return this.index >= this.text.length;
|
|
177362
|
+
}
|
|
177363
|
+
fail(message) {
|
|
177364
|
+
throw new JsonSyntaxError(message, this.position());
|
|
177365
|
+
}
|
|
177366
|
+
position() {
|
|
177367
|
+
return { line: this.line, column: this.column };
|
|
177368
|
+
}
|
|
177369
|
+
skipWhitespace() {
|
|
177370
|
+
while (!this.atEnd()) {
|
|
177371
|
+
const char = this.text[this.index] ?? "";
|
|
177372
|
+
if (char !== " " && char !== "\t" && char !== `
|
|
177373
|
+
` && char !== "\r") {
|
|
177374
|
+
return;
|
|
177375
|
+
}
|
|
177376
|
+
this.advance();
|
|
177377
|
+
}
|
|
177378
|
+
}
|
|
177379
|
+
readValue() {
|
|
177380
|
+
if (this.atEnd()) {
|
|
177381
|
+
this.fail("Unexpected end of input where a JSON value was expected.");
|
|
177382
|
+
}
|
|
177383
|
+
const char = this.text[this.index] ?? "";
|
|
177384
|
+
switch (char) {
|
|
177385
|
+
case "{":
|
|
177386
|
+
return this.readObject();
|
|
177387
|
+
case "[":
|
|
177388
|
+
return this.readArray();
|
|
177389
|
+
case '"':
|
|
177390
|
+
return this.readString();
|
|
177391
|
+
case "t":
|
|
177392
|
+
return this.readKeyword("true", true);
|
|
177393
|
+
case "f":
|
|
177394
|
+
return this.readKeyword("false", false);
|
|
177395
|
+
case "n":
|
|
177396
|
+
return this.readNull();
|
|
177397
|
+
default:
|
|
177398
|
+
return this.readNumber();
|
|
177399
|
+
}
|
|
176886
177400
|
}
|
|
176887
|
-
|
|
176888
|
-
|
|
176889
|
-
|
|
176890
|
-
|
|
176891
|
-
|
|
176892
|
-
|
|
176893
|
-
|
|
177401
|
+
readObject() {
|
|
177402
|
+
const start = this.position();
|
|
177403
|
+
this.expect("{");
|
|
177404
|
+
const members = [];
|
|
177405
|
+
this.skipWhitespace();
|
|
177406
|
+
if (this.peek() === "}") {
|
|
177407
|
+
this.advance();
|
|
177408
|
+
return { kind: "object", members, range: { start, end: this.position() } };
|
|
177409
|
+
}
|
|
177410
|
+
for (;; ) {
|
|
177411
|
+
this.skipWhitespace();
|
|
177412
|
+
if (this.peek() !== '"') {
|
|
177413
|
+
this.fail("Expected a quoted object key.");
|
|
177414
|
+
}
|
|
177415
|
+
const keyNode = this.readString();
|
|
177416
|
+
this.skipWhitespace();
|
|
177417
|
+
this.expect(":");
|
|
177418
|
+
this.skipWhitespace();
|
|
177419
|
+
const value = this.readValue();
|
|
177420
|
+
members.push({ key: keyNode.value, keyRange: keyNode.range, value });
|
|
177421
|
+
this.skipWhitespace();
|
|
177422
|
+
const next = this.peek();
|
|
177423
|
+
if (next === ",") {
|
|
177424
|
+
this.advance();
|
|
177425
|
+
continue;
|
|
177426
|
+
}
|
|
177427
|
+
if (next === "}") {
|
|
177428
|
+
this.advance();
|
|
177429
|
+
return { kind: "object", members, range: { start, end: this.position() } };
|
|
177430
|
+
}
|
|
177431
|
+
this.fail("Expected `,` or `}` in the object.");
|
|
177432
|
+
}
|
|
176894
177433
|
}
|
|
176895
|
-
|
|
176896
|
-
|
|
176897
|
-
|
|
176898
|
-
|
|
176899
|
-
|
|
176900
|
-
|
|
176901
|
-
|
|
176902
|
-
|
|
177434
|
+
readArray() {
|
|
177435
|
+
const start = this.position();
|
|
177436
|
+
this.expect("[");
|
|
177437
|
+
const elements = [];
|
|
177438
|
+
this.skipWhitespace();
|
|
177439
|
+
if (this.peek() === "]") {
|
|
177440
|
+
this.advance();
|
|
177441
|
+
return { kind: "array", elements, range: { start, end: this.position() } };
|
|
177442
|
+
}
|
|
177443
|
+
for (;; ) {
|
|
177444
|
+
this.skipWhitespace();
|
|
177445
|
+
elements.push(this.readValue());
|
|
177446
|
+
this.skipWhitespace();
|
|
177447
|
+
const next = this.peek();
|
|
177448
|
+
if (next === ",") {
|
|
177449
|
+
this.advance();
|
|
177450
|
+
continue;
|
|
177451
|
+
}
|
|
177452
|
+
if (next === "]") {
|
|
177453
|
+
this.advance();
|
|
177454
|
+
return { kind: "array", elements, range: { start, end: this.position() } };
|
|
177455
|
+
}
|
|
177456
|
+
this.fail("Expected `,` or `]` in the array.");
|
|
177457
|
+
}
|
|
176903
177458
|
}
|
|
176904
|
-
|
|
176905
|
-
|
|
176906
|
-
|
|
176907
|
-
|
|
176908
|
-
|
|
176909
|
-
|
|
176910
|
-
|
|
176911
|
-
|
|
176912
|
-
|
|
176913
|
-
|
|
176914
|
-
|
|
176915
|
-
|
|
176916
|
-
|
|
176917
|
-
|
|
177459
|
+
readString() {
|
|
177460
|
+
const start = this.position();
|
|
177461
|
+
this.expect('"');
|
|
177462
|
+
const contentStart = this.position();
|
|
177463
|
+
let value = "";
|
|
177464
|
+
for (;; ) {
|
|
177465
|
+
if (this.atEnd()) {
|
|
177466
|
+
this.fail("Unterminated string.");
|
|
177467
|
+
}
|
|
177468
|
+
const char = this.text[this.index] ?? "";
|
|
177469
|
+
if (char === '"') {
|
|
177470
|
+
const contentEnd = this.position();
|
|
177471
|
+
this.advance();
|
|
177472
|
+
return {
|
|
177473
|
+
kind: "string",
|
|
177474
|
+
value,
|
|
177475
|
+
contentRange: { start: contentStart, end: contentEnd },
|
|
177476
|
+
range: { start, end: this.position() }
|
|
177477
|
+
};
|
|
177478
|
+
}
|
|
177479
|
+
if (char === "\\") {
|
|
177480
|
+
this.advance();
|
|
177481
|
+
value += this.readEscape();
|
|
177482
|
+
continue;
|
|
177483
|
+
}
|
|
177484
|
+
if (char < " ") {
|
|
177485
|
+
this.fail("Unescaped control character in a string.");
|
|
177486
|
+
}
|
|
177487
|
+
this.advance();
|
|
177488
|
+
value += char;
|
|
177489
|
+
}
|
|
177490
|
+
}
|
|
177491
|
+
readEscape() {
|
|
177492
|
+
if (this.atEnd()) {
|
|
177493
|
+
this.fail("Unterminated escape sequence.");
|
|
177494
|
+
}
|
|
177495
|
+
const char = this.text[this.index] ?? "";
|
|
177496
|
+
const simple = ESCAPE_VALUES[char];
|
|
177497
|
+
if (simple !== undefined) {
|
|
177498
|
+
this.advance();
|
|
177499
|
+
return simple;
|
|
177500
|
+
}
|
|
177501
|
+
if (char !== "u") {
|
|
177502
|
+
this.fail(`Unknown escape sequence \`\\${char}\`.`);
|
|
177503
|
+
}
|
|
177504
|
+
this.advance();
|
|
177505
|
+
const digits = this.text.slice(this.index, this.index + 4);
|
|
177506
|
+
if (!/^[0-9a-fA-F]{4}$/.test(digits)) {
|
|
177507
|
+
this.fail("A `\\u` escape needs four hexadecimal digits.");
|
|
177508
|
+
}
|
|
177509
|
+
for (let offset = 0;offset < 4; offset += 1) {
|
|
177510
|
+
this.advance();
|
|
177511
|
+
}
|
|
177512
|
+
return String.fromCharCode(Number.parseInt(digits, 16));
|
|
177513
|
+
}
|
|
177514
|
+
readNumber() {
|
|
177515
|
+
const start = this.position();
|
|
177516
|
+
const pattern = /-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?/y;
|
|
177517
|
+
pattern.lastIndex = this.index;
|
|
177518
|
+
const match = pattern.exec(this.text);
|
|
177519
|
+
if (match === null || match[0].length === 0) {
|
|
177520
|
+
this.fail("Expected a JSON value.");
|
|
177521
|
+
}
|
|
177522
|
+
for (let offset = 0;offset < match[0].length; offset += 1) {
|
|
177523
|
+
this.advance();
|
|
177524
|
+
}
|
|
177525
|
+
if (/[0-9a-zA-Z.+-]/.test(this.peek() ?? "")) {
|
|
177526
|
+
this.fail("Malformed number.");
|
|
177527
|
+
}
|
|
177528
|
+
return { kind: "number", value: Number(match[0]), range: { start, end: this.position() } };
|
|
177529
|
+
}
|
|
177530
|
+
readNull() {
|
|
177531
|
+
const start = this.position();
|
|
177532
|
+
this.consumeWord("null");
|
|
177533
|
+
return { kind: "null", value: null, range: { start, end: this.position() } };
|
|
177534
|
+
}
|
|
177535
|
+
readKeyword(word, value) {
|
|
177536
|
+
const start = this.position();
|
|
177537
|
+
this.consumeWord(word);
|
|
177538
|
+
return { kind: "boolean", value, range: { start, end: this.position() } };
|
|
177539
|
+
}
|
|
177540
|
+
consumeWord(word) {
|
|
177541
|
+
if (this.text.slice(this.index, this.index + word.length) !== word) {
|
|
177542
|
+
this.fail(`Expected \`${word}\`.`);
|
|
177543
|
+
}
|
|
177544
|
+
for (let offset = 0;offset < word.length; offset += 1) {
|
|
177545
|
+
this.advance();
|
|
177546
|
+
}
|
|
177547
|
+
}
|
|
177548
|
+
peek() {
|
|
177549
|
+
return this.text[this.index];
|
|
177550
|
+
}
|
|
177551
|
+
expect(char) {
|
|
177552
|
+
if (this.peek() !== char) {
|
|
177553
|
+
this.fail(`Expected \`${char}\`.`);
|
|
177554
|
+
}
|
|
177555
|
+
this.advance();
|
|
177556
|
+
}
|
|
177557
|
+
advance() {
|
|
177558
|
+
const char = this.text[this.index] ?? "";
|
|
177559
|
+
this.index += 1;
|
|
177560
|
+
if (char === `
|
|
177561
|
+
`) {
|
|
177562
|
+
this.line += 1;
|
|
177563
|
+
this.column = 1;
|
|
177564
|
+
return;
|
|
177565
|
+
}
|
|
177566
|
+
if (char === "\r") {
|
|
177567
|
+
if (this.text[this.index] === `
|
|
177568
|
+
`) {
|
|
177569
|
+
this.index += 1;
|
|
177570
|
+
}
|
|
177571
|
+
this.line += 1;
|
|
177572
|
+
this.column = 1;
|
|
177573
|
+
return;
|
|
177574
|
+
}
|
|
177575
|
+
this.column += 1;
|
|
176918
177576
|
}
|
|
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
177577
|
}
|
|
176941
177578
|
|
|
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) {
|
|
177579
|
+
// src/core/link-manifest.ts
|
|
177580
|
+
var LINK_MANIFEST_FILE_NAME = "docbridge.links.json";
|
|
177581
|
+
var KNOWN_TOP_LEVEL_KEYS2 = new Set(["$schema", "links"]);
|
|
177582
|
+
var KNOWN_ENTRY_KEYS = new Set(["code", "doc", "note"]);
|
|
177583
|
+
var EMPTY_MANIFEST = { entries: [] };
|
|
177584
|
+
function loadLinkManifest(projectRoot) {
|
|
176963
177585
|
let rawText;
|
|
176964
177586
|
try {
|
|
176965
|
-
rawText =
|
|
176966
|
-
} catch {
|
|
177587
|
+
rawText = readFileSync3(join5(projectRoot, LINK_MANIFEST_FILE_NAME), "utf8");
|
|
177588
|
+
} catch (error) {
|
|
177589
|
+
if (!isMissingFileError(error)) {
|
|
177590
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
177591
|
+
return {
|
|
177592
|
+
ok: false,
|
|
177593
|
+
diagnostics: [
|
|
177594
|
+
manifestDiagnostic("config_file_invalid", `Failed to read ${LINK_MANIFEST_FILE_NAME}: ${reason}`)
|
|
177595
|
+
]
|
|
177596
|
+
};
|
|
177597
|
+
}
|
|
176967
177598
|
rawText = undefined;
|
|
176968
177599
|
}
|
|
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
|
-
};
|
|
177600
|
+
return resolveLinkManifest(rawText);
|
|
176982
177601
|
}
|
|
176983
|
-
function
|
|
177602
|
+
function isMissingFileError(error) {
|
|
177603
|
+
return error instanceof Error && error.code === "ENOENT";
|
|
177604
|
+
}
|
|
177605
|
+
function resolveLinkManifest(rawText) {
|
|
176984
177606
|
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
|
-
};
|
|
177607
|
+
return { ok: true, manifest: EMPTY_MANIFEST, diagnostics: [] };
|
|
176992
177608
|
}
|
|
176993
|
-
|
|
176994
|
-
|
|
176995
|
-
parsed = JSON.parse(rawText);
|
|
176996
|
-
} catch (error) {
|
|
176997
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
177609
|
+
const parsed = parseJsonSource(rawText);
|
|
177610
|
+
if (!parsed.ok) {
|
|
176998
177611
|
return {
|
|
176999
|
-
config: EMPTY_CONFIG,
|
|
177000
177612
|
ok: false,
|
|
177001
177613
|
diagnostics: [
|
|
177002
|
-
|
|
177614
|
+
manifestDiagnostic("config_file_invalid", `Failed to parse ${LINK_MANIFEST_FILE_NAME}: ${parsed.message}`, parsed.position)
|
|
177003
177615
|
]
|
|
177004
177616
|
};
|
|
177005
177617
|
}
|
|
177006
|
-
|
|
177007
|
-
|
|
177008
|
-
|
|
177009
|
-
|
|
177618
|
+
return validateManifest(parsed.root);
|
|
177619
|
+
}
|
|
177620
|
+
function validateManifest(root) {
|
|
177621
|
+
if (root.kind !== "object") {
|
|
177622
|
+
return { ok: false, diagnostics: [invalidValue("", "an object", root)] };
|
|
177010
177623
|
}
|
|
177011
|
-
|
|
177012
|
-
|
|
177013
|
-
|
|
177624
|
+
const diagnostics = [];
|
|
177625
|
+
for (const member of root.members) {
|
|
177626
|
+
if (!KNOWN_TOP_LEVEL_KEYS2.has(member.key)) {
|
|
177627
|
+
diagnostics.push(unknownKey(member.key, member));
|
|
177014
177628
|
}
|
|
177015
177629
|
}
|
|
177016
|
-
|
|
177017
|
-
|
|
177630
|
+
reportDuplicateKeys(root, "", diagnostics);
|
|
177631
|
+
const links = root.members.find((member) => member.key === "links");
|
|
177632
|
+
if (links === undefined) {
|
|
177633
|
+
return {
|
|
177634
|
+
ok: false,
|
|
177635
|
+
diagnostics: [...diagnostics, invalidValue("links", "present", root)]
|
|
177636
|
+
};
|
|
177018
177637
|
}
|
|
177019
|
-
|
|
177020
|
-
|
|
177021
|
-
|
|
177022
|
-
|
|
177638
|
+
if (links.value.kind !== "array") {
|
|
177639
|
+
return {
|
|
177640
|
+
ok: false,
|
|
177641
|
+
diagnostics: [...diagnostics, invalidValue("links", "an array", links.value)]
|
|
177642
|
+
};
|
|
177023
177643
|
}
|
|
177024
|
-
|
|
177025
|
-
|
|
177026
|
-
|
|
177644
|
+
const entries = [];
|
|
177645
|
+
for (const [index, element] of links.value.elements.entries()) {
|
|
177646
|
+
const outcome = readEntry(element, index);
|
|
177647
|
+
diagnostics.push(...outcome.diagnostics);
|
|
177648
|
+
if (outcome.entry !== undefined) {
|
|
177649
|
+
entries.push(outcome.entry);
|
|
177027
177650
|
}
|
|
177028
177651
|
}
|
|
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 {};
|
|
177048
|
-
}
|
|
177049
|
-
const result = {};
|
|
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
|
-
}
|
|
177652
|
+
if (diagnostics.some(isBlocking)) {
|
|
177653
|
+
return { ok: false, diagnostics };
|
|
177059
177654
|
}
|
|
177060
|
-
return
|
|
177655
|
+
return { ok: true, manifest: { entries }, diagnostics };
|
|
177061
177656
|
}
|
|
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;
|
|
177657
|
+
function readEntry(element, index) {
|
|
177658
|
+
const path = `links[${index}]`;
|
|
177659
|
+
if (element.kind !== "object") {
|
|
177660
|
+
return { diagnostics: [invalidValue(path, "an object", element)] };
|
|
177084
177661
|
}
|
|
177085
|
-
const
|
|
177086
|
-
|
|
177087
|
-
|
|
177662
|
+
const diagnostics = [];
|
|
177663
|
+
for (const member of element.members) {
|
|
177664
|
+
if (!KNOWN_ENTRY_KEYS.has(member.key)) {
|
|
177665
|
+
diagnostics.push(unknownKey(`${path}.${member.key}`, member));
|
|
177666
|
+
}
|
|
177667
|
+
}
|
|
177668
|
+
reportDuplicateKeys(element, `${path}.`, diagnostics);
|
|
177669
|
+
const code = readString(element, "code", path, diagnostics);
|
|
177670
|
+
const doc = readString(element, "doc", path, diagnostics);
|
|
177671
|
+
const note = readOptionalNote(element, path, diagnostics);
|
|
177672
|
+
if (code === undefined || doc === undefined || diagnostics.some(isBlocking)) {
|
|
177673
|
+
return { diagnostics };
|
|
177674
|
+
}
|
|
177675
|
+
const codeTarget = parseTarget(code, diagnostics);
|
|
177676
|
+
const docTarget = parseTarget(doc, diagnostics);
|
|
177677
|
+
if (codeTarget === undefined || docTarget === undefined) {
|
|
177678
|
+
return { diagnostics };
|
|
177679
|
+
}
|
|
177680
|
+
const entry = {
|
|
177681
|
+
code: codeTarget,
|
|
177682
|
+
doc: docTarget,
|
|
177683
|
+
codeEndpoint: code.value,
|
|
177684
|
+
docEndpoint: doc.value,
|
|
177685
|
+
location: positionOf(element.range),
|
|
177686
|
+
range: element.range,
|
|
177687
|
+
codeRange: code.contentRange,
|
|
177688
|
+
docRange: doc.contentRange
|
|
177689
|
+
};
|
|
177690
|
+
if (note !== undefined) {
|
|
177691
|
+
entry.note = note;
|
|
177088
177692
|
}
|
|
177089
|
-
return entry;
|
|
177693
|
+
return { entry, diagnostics };
|
|
177090
177694
|
}
|
|
177091
|
-
function
|
|
177092
|
-
const
|
|
177093
|
-
if (
|
|
177094
|
-
diagnostics.push(
|
|
177695
|
+
function readString(entry, key, path, diagnostics) {
|
|
177696
|
+
const member = entry.members.find((candidate) => candidate.key === key);
|
|
177697
|
+
if (member === undefined) {
|
|
177698
|
+
diagnostics.push(invalidValue(`${path}.${key}`, "present", entry));
|
|
177095
177699
|
return;
|
|
177096
177700
|
}
|
|
177097
|
-
|
|
177098
|
-
|
|
177099
|
-
|
|
177100
|
-
}
|
|
177701
|
+
if (member.value.kind !== "string") {
|
|
177702
|
+
diagnostics.push(invalidValue(`${path}.${key}`, "a string", member.value));
|
|
177703
|
+
return;
|
|
177101
177704
|
}
|
|
177705
|
+
return member.value;
|
|
177102
177706
|
}
|
|
177103
|
-
function
|
|
177104
|
-
const
|
|
177105
|
-
if (
|
|
177106
|
-
diagnostics.push(configDiagnostic("config_invalid_value", target, `\`${target}\` must be a non-empty array of strings.`));
|
|
177707
|
+
function readOptionalNote(entry, path, diagnostics) {
|
|
177708
|
+
const member = entry.members.find((candidate) => candidate.key === "note");
|
|
177709
|
+
if (member === undefined) {
|
|
177107
177710
|
return;
|
|
177108
177711
|
}
|
|
177109
|
-
if (value.
|
|
177110
|
-
diagnostics.push(
|
|
177712
|
+
if (member.value.kind !== "string") {
|
|
177713
|
+
diagnostics.push(invalidValue(`${path}.note`, "a string", member.value));
|
|
177111
177714
|
return;
|
|
177112
177715
|
}
|
|
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
|
-
}
|
|
177716
|
+
return member.value.value;
|
|
177128
177717
|
}
|
|
177129
|
-
function
|
|
177130
|
-
|
|
177131
|
-
|
|
177132
|
-
|
|
177133
|
-
|
|
177134
|
-
|
|
177718
|
+
function parseTarget(node, diagnostics) {
|
|
177719
|
+
const result = parseLinkTarget(node.value, {
|
|
177720
|
+
location: positionOf(node.contentRange),
|
|
177721
|
+
targetRange: node.contentRange
|
|
177722
|
+
});
|
|
177723
|
+
if (result.ok) {
|
|
177724
|
+
return result.target;
|
|
177135
177725
|
}
|
|
177726
|
+
diagnostics.push(result.diagnostic);
|
|
177136
177727
|
return;
|
|
177137
177728
|
}
|
|
177138
|
-
function
|
|
177139
|
-
const
|
|
177140
|
-
for (const
|
|
177141
|
-
if (
|
|
177142
|
-
diagnostics.push(
|
|
177729
|
+
function reportDuplicateKeys(object, pathPrefix, diagnostics) {
|
|
177730
|
+
const seen = new Set;
|
|
177731
|
+
for (const member of object.members) {
|
|
177732
|
+
if (seen.has(member.key)) {
|
|
177733
|
+
diagnostics.push(manifestDiagnostic("config_invalid_value", `Duplicate key ${pathPrefix}${member.key} in ${LINK_MANIFEST_FILE_NAME}.`, positionOf(member.keyRange), member.keyRange));
|
|
177143
177734
|
}
|
|
177735
|
+
seen.add(member.key);
|
|
177144
177736
|
}
|
|
177145
|
-
return diagnostics;
|
|
177146
177737
|
}
|
|
177147
|
-
function
|
|
177148
|
-
return
|
|
177738
|
+
function isBlocking(diagnostic) {
|
|
177739
|
+
return diagnostic.code !== "invalid_link_target";
|
|
177149
177740
|
}
|
|
177150
|
-
function
|
|
177151
|
-
return
|
|
177741
|
+
function unknownKey(path, member) {
|
|
177742
|
+
return manifestDiagnostic("config_unknown_key", `Unknown key ${path} in ${LINK_MANIFEST_FILE_NAME}.`, positionOf(member.keyRange), member.keyRange);
|
|
177152
177743
|
}
|
|
177153
|
-
function
|
|
177154
|
-
|
|
177155
|
-
}
|
|
177156
|
-
|
|
177157
|
-
// src/core/diagnostics.ts
|
|
177158
|
-
function sortDiagnostics(diagnostics) {
|
|
177159
|
-
return [...diagnostics].toSorted(compareDiagnostics);
|
|
177744
|
+
function invalidValue(path, expectation, node) {
|
|
177745
|
+
const subject = path === "" ? LINK_MANIFEST_FILE_NAME : path;
|
|
177746
|
+
return manifestDiagnostic("config_invalid_value", `${subject} must be ${expectation}.`, positionOf(node.range), node.range);
|
|
177160
177747
|
}
|
|
177161
|
-
function
|
|
177162
|
-
const
|
|
177163
|
-
|
|
177164
|
-
|
|
177165
|
-
|
|
177166
|
-
|
|
177167
|
-
|
|
177168
|
-
|
|
177748
|
+
function manifestDiagnostic(code, message, position, range) {
|
|
177749
|
+
const diagnostic = {
|
|
177750
|
+
severity: "error",
|
|
177751
|
+
code,
|
|
177752
|
+
target: LINK_MANIFEST_FILE_NAME,
|
|
177753
|
+
message
|
|
177754
|
+
};
|
|
177755
|
+
if (position !== undefined) {
|
|
177756
|
+
diagnostic.location = {
|
|
177757
|
+
filePath: LINK_MANIFEST_FILE_NAME,
|
|
177758
|
+
line: position.line,
|
|
177759
|
+
column: position.column
|
|
177760
|
+
};
|
|
177169
177761
|
}
|
|
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}`;
|
|
177762
|
+
if (range !== undefined) {
|
|
177763
|
+
diagnostic.range = range;
|
|
177176
177764
|
}
|
|
177177
|
-
|
|
177178
|
-
return `${prefix} ${diagnostic.severity} ${diagnostic.code} ${diagnostic.target} - ${diagnostic.message}`;
|
|
177765
|
+
return diagnostic;
|
|
177179
177766
|
}
|
|
177180
|
-
function
|
|
177181
|
-
return
|
|
177767
|
+
function positionOf(range) {
|
|
177768
|
+
return {
|
|
177769
|
+
filePath: LINK_MANIFEST_FILE_NAME,
|
|
177770
|
+
line: range.start.line,
|
|
177771
|
+
column: range.start.column
|
|
177772
|
+
};
|
|
177182
177773
|
}
|
|
177183
|
-
|
|
177184
|
-
|
|
177185
|
-
|
|
177186
|
-
|
|
177187
|
-
|
|
177188
|
-
|
|
177189
|
-
|
|
177190
|
-
|
|
177774
|
+
|
|
177775
|
+
// src/core/suggest.ts
|
|
177776
|
+
function nearestMatch(input, candidates) {
|
|
177777
|
+
const ranked = candidates.map((candidate, index) => ({
|
|
177778
|
+
candidate,
|
|
177779
|
+
distance: editDistance(input, candidate),
|
|
177780
|
+
index
|
|
177781
|
+
})).toSorted((left, right) => left.distance - right.distance || left.index - right.index);
|
|
177782
|
+
const abbreviation = ranked.find(({ candidate }) => isOrderedAbbreviation(input, candidate));
|
|
177783
|
+
if (abbreviation !== undefined) {
|
|
177784
|
+
return abbreviation.candidate;
|
|
177191
177785
|
}
|
|
177192
|
-
|
|
177193
|
-
|
|
177786
|
+
const best = ranked[0];
|
|
177787
|
+
if (best === undefined) {
|
|
177788
|
+
return;
|
|
177194
177789
|
}
|
|
177195
|
-
|
|
177196
|
-
|
|
177197
|
-
function compareString(left, right) {
|
|
177198
|
-
return left.localeCompare(right);
|
|
177790
|
+
const closeEnough = best.distance <= Math.max(1, Math.floor(best.candidate.length / 2));
|
|
177791
|
+
return closeEnough ? best.candidate : undefined;
|
|
177199
177792
|
}
|
|
177200
|
-
function
|
|
177201
|
-
|
|
177793
|
+
function isOrderedAbbreviation(input, candidate) {
|
|
177794
|
+
if (input.length < 3 || input.length >= candidate.length) {
|
|
177795
|
+
return false;
|
|
177796
|
+
}
|
|
177797
|
+
let candidateIndex = 0;
|
|
177798
|
+
for (const character of input) {
|
|
177799
|
+
const matchIndex = candidate.indexOf(character, candidateIndex);
|
|
177800
|
+
if (matchIndex === -1) {
|
|
177801
|
+
return false;
|
|
177802
|
+
}
|
|
177803
|
+
candidateIndex = matchIndex + 1;
|
|
177804
|
+
}
|
|
177805
|
+
return true;
|
|
177202
177806
|
}
|
|
177203
|
-
function
|
|
177204
|
-
|
|
177807
|
+
function editDistance(left, right) {
|
|
177808
|
+
let previous = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
177809
|
+
for (let leftIndex = 1;leftIndex <= left.length; leftIndex += 1) {
|
|
177810
|
+
const current = [leftIndex];
|
|
177811
|
+
for (let rightIndex = 1;rightIndex <= right.length; rightIndex += 1) {
|
|
177812
|
+
const substitutionCost = left[leftIndex - 1] === right[rightIndex - 1] ? 0 : 1;
|
|
177813
|
+
current[rightIndex] = Math.min((current[rightIndex - 1] ?? Number.POSITIVE_INFINITY) + 1, (previous[rightIndex] ?? Number.POSITIVE_INFINITY) + 1, (previous[rightIndex - 1] ?? Number.POSITIVE_INFINITY) + substitutionCost);
|
|
177814
|
+
}
|
|
177815
|
+
previous = current;
|
|
177816
|
+
}
|
|
177817
|
+
return previous[right.length] ?? 0;
|
|
177205
177818
|
}
|
|
177206
177819
|
|
|
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;
|
|
177820
|
+
// src/core/link-manifest-apply.ts
|
|
177821
|
+
function applyLinkManifest(input) {
|
|
177822
|
+
if (input.manifest.entries.length === 0) {
|
|
177823
|
+
return { codeFiles: input.codeFiles, docFiles: input.docFiles, diagnostics: [] };
|
|
177223
177824
|
}
|
|
177224
|
-
const
|
|
177225
|
-
|
|
177825
|
+
const state = new ApplyState(input);
|
|
177826
|
+
for (const entry of input.manifest.entries) {
|
|
177827
|
+
state.applyEntry(entry);
|
|
177828
|
+
}
|
|
177829
|
+
return state.result();
|
|
177226
177830
|
}
|
|
177227
177831
|
|
|
177228
|
-
|
|
177229
|
-
|
|
177230
|
-
|
|
177231
|
-
|
|
177232
|
-
|
|
177233
|
-
|
|
177234
|
-
|
|
177235
|
-
|
|
177832
|
+
class ApplyState {
|
|
177833
|
+
erroredFiles;
|
|
177834
|
+
codeStates = new Map;
|
|
177835
|
+
docStates = new Map;
|
|
177836
|
+
codeOrder;
|
|
177837
|
+
docOrder;
|
|
177838
|
+
diagnostics = [];
|
|
177839
|
+
constructor(input) {
|
|
177840
|
+
this.erroredFiles = collectErroredFiles(input.scanDiagnostics);
|
|
177841
|
+
this.codeOrder = input.codeFiles;
|
|
177842
|
+
this.docOrder = input.docFiles;
|
|
177843
|
+
for (const file of input.codeFiles) {
|
|
177844
|
+
const byEndpoint = new Map;
|
|
177845
|
+
for (const symbol of [...file.symbols, ...file.undocumentedSymbols]) {
|
|
177846
|
+
if (!byEndpoint.has(symbol.endpoint)) {
|
|
177847
|
+
byEndpoint.set(symbol.endpoint, symbol);
|
|
177848
|
+
}
|
|
177849
|
+
}
|
|
177850
|
+
this.codeStates.set(file.filePath, {
|
|
177851
|
+
file,
|
|
177852
|
+
symbols: file.symbols,
|
|
177853
|
+
undocumentedSymbols: file.undocumentedSymbols,
|
|
177854
|
+
links: file.links,
|
|
177855
|
+
byEndpoint,
|
|
177856
|
+
outgoing: new Set(file.links.map((annotation) => pairKey(annotation.source, annotation.target))),
|
|
177857
|
+
changed: false
|
|
177858
|
+
});
|
|
177859
|
+
}
|
|
177860
|
+
for (const file of input.docFiles) {
|
|
177861
|
+
this.docStates.set(file.filePath, {
|
|
177862
|
+
file,
|
|
177863
|
+
headings: file.headings,
|
|
177864
|
+
links: file.links,
|
|
177865
|
+
anchors: new Set(file.anchors.map((anchor) => anchor.endpoint)),
|
|
177866
|
+
outgoing: new Set(file.links.map((annotation) => pairKey(annotation.source, annotation.target))),
|
|
177867
|
+
changed: false
|
|
177868
|
+
});
|
|
177236
177869
|
}
|
|
177237
177870
|
}
|
|
177238
|
-
|
|
177239
|
-
|
|
177240
|
-
|
|
177241
|
-
|
|
177242
|
-
|
|
177243
|
-
|
|
177871
|
+
applyEntry(entry) {
|
|
177872
|
+
if (this.erroredFiles.has(entry.code.filePath) || this.erroredFiles.has(entry.doc.filePath)) {
|
|
177873
|
+
return;
|
|
177874
|
+
}
|
|
177875
|
+
const codeResolved = this.resolveCodeSide(entry);
|
|
177876
|
+
const docResolved = this.resolveDocSide(entry);
|
|
177877
|
+
if (!codeResolved || !docResolved) {
|
|
177878
|
+
return;
|
|
177244
177879
|
}
|
|
177880
|
+
this.addLinks(entry);
|
|
177245
177881
|
}
|
|
177246
|
-
|
|
177247
|
-
|
|
177248
|
-
|
|
177249
|
-
|
|
177250
|
-
|
|
177251
|
-
|
|
177252
|
-
|
|
177253
|
-
|
|
177254
|
-
|
|
177255
|
-
|
|
177882
|
+
resolveCodeSide(entry) {
|
|
177883
|
+
const state = this.codeStates.get(entry.code.filePath);
|
|
177884
|
+
if (state === undefined) {
|
|
177885
|
+
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");
|
|
177886
|
+
return false;
|
|
177887
|
+
}
|
|
177888
|
+
const symbol = state.byEndpoint.get(entry.codeEndpoint);
|
|
177889
|
+
if (symbol === undefined) {
|
|
177890
|
+
this.report("code_symbol_not_found", entry.codeEndpoint, this.symbolNotFoundMessage(entry, state), entry, "code", state.file.language);
|
|
177891
|
+
return false;
|
|
177256
177892
|
}
|
|
177893
|
+
this.markDocumented(state, symbol);
|
|
177894
|
+
return true;
|
|
177257
177895
|
}
|
|
177258
|
-
|
|
177259
|
-
|
|
177260
|
-
|
|
177261
|
-
|
|
177262
|
-
|
|
177896
|
+
resolveDocSide(entry) {
|
|
177897
|
+
const state = this.docStates.get(entry.doc.filePath);
|
|
177898
|
+
if (state === undefined) {
|
|
177899
|
+
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");
|
|
177900
|
+
return false;
|
|
177901
|
+
}
|
|
177902
|
+
if (!state.anchors.has(entry.docEndpoint)) {
|
|
177903
|
+
this.report("doc_anchor_not_found", entry.docEndpoint, `Doc anchor ${entry.docEndpoint} declared in the link manifest does not exist.`, entry, "doc");
|
|
177904
|
+
return false;
|
|
177263
177905
|
}
|
|
177906
|
+
this.markAnnotated(state, entry.docEndpoint);
|
|
177907
|
+
return true;
|
|
177264
177908
|
}
|
|
177265
|
-
|
|
177266
|
-
|
|
177267
|
-
|
|
177268
|
-
|
|
177269
|
-
|
|
177270
|
-
|
|
177909
|
+
addLinks(entry) {
|
|
177910
|
+
const codeState = this.codeStates.get(entry.code.filePath);
|
|
177911
|
+
const docState = this.docStates.get(entry.doc.filePath);
|
|
177912
|
+
if (codeState === undefined || docState === undefined) {
|
|
177913
|
+
return;
|
|
177914
|
+
}
|
|
177915
|
+
const forward = pairKey(entry.codeEndpoint, entry.docEndpoint);
|
|
177916
|
+
const backward = pairKey(entry.docEndpoint, entry.codeEndpoint);
|
|
177917
|
+
const hasForward = codeState.outgoing.has(forward);
|
|
177918
|
+
const hasBackward = docState.outgoing.has(backward);
|
|
177919
|
+
if (hasForward || hasBackward) {
|
|
177920
|
+
this.diagnostics.push({
|
|
177921
|
+
severity: "warning",
|
|
177922
|
+
code: "duplicate_link",
|
|
177923
|
+
source: entry.codeEndpoint,
|
|
177924
|
+
target: entry.docEndpoint,
|
|
177925
|
+
message: `Link ${entry.codeEndpoint} to ${entry.docEndpoint} is already declared.`,
|
|
177926
|
+
location: entry.location,
|
|
177927
|
+
range: entry.range
|
|
177928
|
+
});
|
|
177929
|
+
}
|
|
177930
|
+
if (!hasForward) {
|
|
177931
|
+
codeState.links = [
|
|
177932
|
+
...codeState.links,
|
|
177933
|
+
manifestLink(entry.codeEndpoint, entry.docEndpoint, entry, "doc")
|
|
177934
|
+
];
|
|
177935
|
+
codeState.outgoing.add(forward);
|
|
177936
|
+
codeState.changed = true;
|
|
177937
|
+
}
|
|
177938
|
+
if (!hasBackward) {
|
|
177939
|
+
docState.links = [
|
|
177940
|
+
...docState.links,
|
|
177941
|
+
manifestLink(entry.docEndpoint, entry.codeEndpoint, entry, "code")
|
|
177942
|
+
];
|
|
177943
|
+
docState.outgoing.add(backward);
|
|
177944
|
+
docState.changed = true;
|
|
177945
|
+
}
|
|
177271
177946
|
}
|
|
177272
|
-
|
|
177273
|
-
|
|
177274
|
-
|
|
177275
|
-
if (code !== undefined) {
|
|
177276
|
-
resolved.push(code);
|
|
177277
|
-
continue;
|
|
177947
|
+
markDocumented(state, symbol) {
|
|
177948
|
+
if (!state.undocumentedSymbols.includes(symbol)) {
|
|
177949
|
+
return;
|
|
177278
177950
|
}
|
|
177279
|
-
|
|
177280
|
-
|
|
177281
|
-
|
|
177951
|
+
state.undocumentedSymbols = state.undocumentedSymbols.filter((candidate) => candidate !== symbol);
|
|
177952
|
+
state.symbols = [...state.symbols, symbol];
|
|
177953
|
+
state.changed = true;
|
|
177954
|
+
}
|
|
177955
|
+
markAnnotated(state, endpoint) {
|
|
177956
|
+
const index = state.headings.findIndex((heading2) => heading2.anchor?.endpoint === endpoint);
|
|
177957
|
+
const heading = state.headings[index];
|
|
177958
|
+
if (heading === undefined || heading.hasCodeAnnotation) {
|
|
177959
|
+
return;
|
|
177282
177960
|
}
|
|
177961
|
+
state.headings = state.headings.toSpliced(index, 1, {
|
|
177962
|
+
...heading,
|
|
177963
|
+
hasCodeAnnotation: true
|
|
177964
|
+
});
|
|
177965
|
+
state.changed = true;
|
|
177283
177966
|
}
|
|
177284
|
-
|
|
177285
|
-
}
|
|
177286
|
-
|
|
177287
|
-
|
|
177288
|
-
|
|
177289
|
-
|
|
177290
|
-
|
|
177967
|
+
symbolNotFoundMessage(entry, state) {
|
|
177968
|
+
const base = `Code symbol ${entry.codeEndpoint} declared in the link manifest does not exist.`;
|
|
177969
|
+
const candidates = [...state.byEndpoint.values()].map((symbol) => symbol.canonicalId);
|
|
177970
|
+
const suggestion = nearestMatch(entry.code.fragment, candidates);
|
|
177971
|
+
return suggestion === undefined ? base : `${base} Did you mean \`${suggestion}\`?`;
|
|
177972
|
+
}
|
|
177973
|
+
report(code, target, message, entry, side, language) {
|
|
177974
|
+
const range = side === "code" ? entry.codeRange : entry.docRange;
|
|
177975
|
+
const diagnostic = {
|
|
177976
|
+
severity: "error",
|
|
177977
|
+
code,
|
|
177978
|
+
target,
|
|
177979
|
+
message,
|
|
177980
|
+
location: { ...entry.location, line: range.start.line, column: range.start.column },
|
|
177981
|
+
range
|
|
177982
|
+
};
|
|
177983
|
+
if (language !== undefined) {
|
|
177984
|
+
diagnostic.language = language;
|
|
177985
|
+
}
|
|
177986
|
+
this.diagnostics.push(diagnostic);
|
|
177987
|
+
}
|
|
177988
|
+
result() {
|
|
177989
|
+
const codeFiles = this.codeOrder.map((file) => {
|
|
177990
|
+
const state = this.codeStates.get(file.filePath);
|
|
177991
|
+
if (state === undefined || !state.changed) {
|
|
177992
|
+
return file;
|
|
177993
|
+
}
|
|
177994
|
+
return {
|
|
177995
|
+
...file,
|
|
177996
|
+
symbols: state.symbols,
|
|
177997
|
+
undocumentedSymbols: state.undocumentedSymbols,
|
|
177998
|
+
links: state.links
|
|
177999
|
+
};
|
|
178000
|
+
});
|
|
178001
|
+
const docFiles = this.docOrder.map((file) => {
|
|
178002
|
+
const state = this.docStates.get(file.filePath);
|
|
178003
|
+
if (state === undefined || !state.changed) {
|
|
178004
|
+
return file;
|
|
178005
|
+
}
|
|
178006
|
+
return { ...file, headings: state.headings, links: state.links };
|
|
178007
|
+
});
|
|
178008
|
+
return { codeFiles, docFiles, diagnostics: this.diagnostics };
|
|
177291
178009
|
}
|
|
177292
|
-
existing.add(value);
|
|
177293
178010
|
}
|
|
177294
|
-
function
|
|
177295
|
-
return
|
|
178011
|
+
function pairKey(source, target) {
|
|
178012
|
+
return `${source} -> ${target}`;
|
|
178013
|
+
}
|
|
178014
|
+
function manifestLink(source, target, entry, side) {
|
|
178015
|
+
return {
|
|
178016
|
+
source,
|
|
178017
|
+
target,
|
|
178018
|
+
location: entry.location,
|
|
178019
|
+
targetRange: side === "code" ? entry.codeRange : entry.docRange
|
|
178020
|
+
};
|
|
177296
178021
|
}
|
|
177297
178022
|
|
|
177298
178023
|
// src/core/markdown-syntax.ts
|
|
@@ -177565,10 +178290,17 @@ function scanProject(options) {
|
|
|
177565
178290
|
if (!configResult.ok) {
|
|
177566
178291
|
return { ok: false, diagnostics: configResult.diagnostics };
|
|
177567
178292
|
}
|
|
178293
|
+
const manifestResult = loadLinkManifest(options.projectRoot);
|
|
178294
|
+
if (!manifestResult.ok) {
|
|
178295
|
+
return { ok: false, diagnostics: [...configResult.diagnostics, ...manifestResult.diagnostics] };
|
|
178296
|
+
}
|
|
177568
178297
|
const collectCode = options.collectCode ?? collectCodeFiles;
|
|
177569
178298
|
const collectDocs = options.collectDocs ?? collectFiles;
|
|
177570
178299
|
const readFile = options.readFile ?? ((relPath) => readManagedFile(options.projectRoot, relPath));
|
|
177571
|
-
const diagnostics = [
|
|
178300
|
+
const diagnostics = [
|
|
178301
|
+
...configResult.diagnostics,
|
|
178302
|
+
...manifestResult.diagnostics
|
|
178303
|
+
];
|
|
177572
178304
|
const contentByFile = options.keepContent ? new Map : undefined;
|
|
177573
178305
|
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
178306
|
diagnostics.push(...codeScan.diagnostics);
|
|
@@ -177584,17 +178316,24 @@ function scanProject(options) {
|
|
|
177584
178316
|
diagnostics.push(...scan2.diagnostics);
|
|
177585
178317
|
docFiles.push(scan2);
|
|
177586
178318
|
}
|
|
177587
|
-
const
|
|
178319
|
+
const applied = applyLinkManifest({
|
|
178320
|
+
manifest: manifestResult.manifest,
|
|
178321
|
+
codeFiles: codeScan.codeFiles,
|
|
178322
|
+
docFiles,
|
|
178323
|
+
scanDiagnostics: diagnostics
|
|
178324
|
+
});
|
|
178325
|
+
diagnostics.push(...applied.diagnostics);
|
|
178326
|
+
const codeFiles = applied.codeFiles;
|
|
177588
178327
|
const scan = {
|
|
177589
178328
|
codeFiles,
|
|
177590
|
-
docFiles,
|
|
178329
|
+
docFiles: applied.docFiles,
|
|
177591
178330
|
diagnostics
|
|
177592
178331
|
};
|
|
177593
178332
|
if (contentByFile !== undefined) {
|
|
177594
178333
|
scan.contentByFile = contentByFile;
|
|
177595
178334
|
}
|
|
177596
178335
|
if (options.buildGraph === true) {
|
|
177597
|
-
scan.graph = buildLinkGraph(codeFiles, docFiles);
|
|
178336
|
+
scan.graph = buildLinkGraph(codeFiles, applied.docFiles);
|
|
177598
178337
|
}
|
|
177599
178338
|
return {
|
|
177600
178339
|
ok: true,
|
|
@@ -177743,7 +178482,7 @@ function resolveLinks(input) {
|
|
|
177743
178482
|
continue;
|
|
177744
178483
|
}
|
|
177745
178484
|
for (const link of file.links) {
|
|
177746
|
-
docToCodePairs.add(
|
|
178485
|
+
docToCodePairs.add(pairKey2(link.source, link.target));
|
|
177747
178486
|
}
|
|
177748
178487
|
}
|
|
177749
178488
|
const codeToDocPairs = new Set;
|
|
@@ -177752,7 +178491,7 @@ function resolveLinks(input) {
|
|
|
177752
178491
|
continue;
|
|
177753
178492
|
}
|
|
177754
178493
|
for (const link of file.links) {
|
|
177755
|
-
codeToDocPairs.add(
|
|
178494
|
+
codeToDocPairs.add(pairKey2(link.source, link.target));
|
|
177756
178495
|
}
|
|
177757
178496
|
}
|
|
177758
178497
|
for (const file of input.codeFiles) {
|
|
@@ -177773,7 +178512,7 @@ function resolveLinks(input) {
|
|
|
177773
178512
|
diagnostics.push(relationshipDiagnostic("doc_anchor_not_found", link.source, docEndpoint, `Doc anchor ${docEndpoint} referenced by ${link.source} does not exist.`, link));
|
|
177774
178513
|
continue;
|
|
177775
178514
|
}
|
|
177776
|
-
if (!docToCodePairs.has(
|
|
178515
|
+
if (!docToCodePairs.has(pairKey2(docEndpoint, link.source))) {
|
|
177777
178516
|
diagnostics.push(relationshipDiagnostic("doc_backlink_not_found", link.source, docEndpoint, `Doc anchor ${docEndpoint} has no matching @code backlink to ${link.source}.`, link));
|
|
177778
178517
|
}
|
|
177779
178518
|
}
|
|
@@ -177792,7 +178531,7 @@ function resolveLinks(input) {
|
|
|
177792
178531
|
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
178532
|
continue;
|
|
177794
178533
|
}
|
|
177795
|
-
if (!codeToDocPairs.has(
|
|
178534
|
+
if (!codeToDocPairs.has(pairKey2(codeEndpoint, link.source))) {
|
|
177796
178535
|
diagnostics.push(relationshipDiagnostic("code_backlink_not_found", link.source, codeEndpoint, `Code endpoint ${codeEndpoint} has no matching @doc pair back to ${link.source}.`, link));
|
|
177797
178536
|
}
|
|
177798
178537
|
}
|
|
@@ -177827,6 +178566,9 @@ function auditUndocumentedSymbols(input, erroredFiles) {
|
|
|
177827
178566
|
continue;
|
|
177828
178567
|
}
|
|
177829
178568
|
for (const symbol of file.undocumentedSymbols) {
|
|
178569
|
+
if (symbol.isMember === true) {
|
|
178570
|
+
continue;
|
|
178571
|
+
}
|
|
177830
178572
|
diagnostics.push({
|
|
177831
178573
|
severity: "warning",
|
|
177832
178574
|
code: "undocumented_symbol",
|
|
@@ -177898,19 +178640,7 @@ function unlinkedDocSectionDiagnostic(node, anchor) {
|
|
|
177898
178640
|
}
|
|
177899
178641
|
return diagnostic;
|
|
177900
178642
|
}
|
|
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) {
|
|
178643
|
+
function pairKey2(source, target) {
|
|
177914
178644
|
return `${source}->${target}`;
|
|
177915
178645
|
}
|
|
177916
178646
|
function relationshipDiagnostic(code, source, target, message, link) {
|
|
@@ -178498,8 +179228,33 @@ function comparePairs(left, right) {
|
|
|
178498
179228
|
return left.docEndpoint.localeCompare(right.docEndpoint) || left.codeEndpoint.localeCompare(right.codeEndpoint);
|
|
178499
179229
|
}
|
|
178500
179230
|
|
|
179231
|
+
// src/core/package-root.ts
|
|
179232
|
+
import { existsSync as existsSync2, realpathSync as realpathSync2 } from "node:fs";
|
|
179233
|
+
import { dirname as dirname3, join as join6 } from "node:path";
|
|
179234
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
179235
|
+
function resolvePackageRoot(moduleUrl = import.meta.url) {
|
|
179236
|
+
const modulePath = fileURLToPath2(moduleUrl);
|
|
179237
|
+
let resolved;
|
|
179238
|
+
try {
|
|
179239
|
+
resolved = realpathSync2(modulePath);
|
|
179240
|
+
} catch {
|
|
179241
|
+
resolved = modulePath;
|
|
179242
|
+
}
|
|
179243
|
+
let dir = dirname3(resolved);
|
|
179244
|
+
for (;; ) {
|
|
179245
|
+
if (existsSync2(join6(dir, "templates", "skills"))) {
|
|
179246
|
+
return dir;
|
|
179247
|
+
}
|
|
179248
|
+
const parent = dirname3(dir);
|
|
179249
|
+
if (parent === dir) {
|
|
179250
|
+
return dir;
|
|
179251
|
+
}
|
|
179252
|
+
dir = parent;
|
|
179253
|
+
}
|
|
179254
|
+
}
|
|
179255
|
+
|
|
178501
179256
|
// src/lsp/server.ts
|
|
178502
|
-
import { fileURLToPath as
|
|
179257
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
178503
179258
|
|
|
178504
179259
|
// src/lsp/position.ts
|
|
178505
179260
|
function toLspPosition(position) {
|
|
@@ -178677,11 +179432,11 @@ function counterpartLocators(state, filePath, position) {
|
|
|
178677
179432
|
|
|
178678
179433
|
// src/lsp/paths.ts
|
|
178679
179434
|
import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2, sep } from "node:path";
|
|
178680
|
-
import { fileURLToPath as
|
|
179435
|
+
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
178681
179436
|
function uriToRelativePath(projectRoot, uri) {
|
|
178682
179437
|
let absolute;
|
|
178683
179438
|
try {
|
|
178684
|
-
absolute =
|
|
179439
|
+
absolute = fileURLToPath3(uri);
|
|
178685
179440
|
} catch {
|
|
178686
179441
|
return;
|
|
178687
179442
|
}
|
|
@@ -179022,523 +179777,380 @@ class Server {
|
|
|
179022
179777
|
}
|
|
179023
179778
|
scheduleFlush() {
|
|
179024
179779
|
this.dirty = true;
|
|
179025
|
-
this.clearTimer();
|
|
179026
|
-
this.timer = setTimeout(() => {
|
|
179027
|
-
this.timer = null;
|
|
179028
|
-
this.flush();
|
|
179029
|
-
}, this.debounceMs);
|
|
179030
|
-
}
|
|
179031
|
-
flush() {
|
|
179032
|
-
this.clearTimer();
|
|
179033
|
-
if (this.project === null || !this.dirty) {
|
|
179034
|
-
return;
|
|
179035
|
-
}
|
|
179036
|
-
this.dirty = false;
|
|
179037
|
-
const state = this.project.resolve();
|
|
179038
|
-
for (const rel of this.openFiles) {
|
|
179039
|
-
const content = state.contentByFile.get(rel) ?? "";
|
|
179040
|
-
this.publish(rel, diagnosticsForFile(state.diagnostics, rel, content));
|
|
179041
|
-
}
|
|
179042
|
-
}
|
|
179043
|
-
publish(rel, diagnostics) {
|
|
179044
|
-
if (this.project === null) {
|
|
179045
|
-
return;
|
|
179046
|
-
}
|
|
179047
|
-
this.send({
|
|
179048
|
-
jsonrpc: "2.0",
|
|
179049
|
-
method: "textDocument/publishDiagnostics",
|
|
179050
|
-
params: { uri: relativePathToUri(this.project.root, rel), diagnostics }
|
|
179051
|
-
});
|
|
179052
|
-
}
|
|
179053
|
-
relPath(uri) {
|
|
179054
|
-
if (uri === undefined || this.project === null) {
|
|
179055
|
-
return;
|
|
179056
|
-
}
|
|
179057
|
-
return uriToRelativePath(this.project.root, uri);
|
|
179058
|
-
}
|
|
179059
|
-
respond(id, result) {
|
|
179060
|
-
if (id === undefined) {
|
|
179061
|
-
return;
|
|
179062
|
-
}
|
|
179063
|
-
this.send({ jsonrpc: "2.0", id, result });
|
|
179064
|
-
}
|
|
179065
|
-
clearTimer() {
|
|
179066
|
-
if (this.timer !== null) {
|
|
179067
|
-
clearTimeout(this.timer);
|
|
179068
|
-
this.timer = null;
|
|
179069
|
-
}
|
|
179070
|
-
}
|
|
179071
|
-
}
|
|
179072
|
-
function resolveRoot(params) {
|
|
179073
|
-
const record = params;
|
|
179074
|
-
const folderUri = record?.workspaceFolders?.[0]?.uri;
|
|
179075
|
-
if (typeof folderUri === "string") {
|
|
179076
|
-
const path = uriToPath(folderUri);
|
|
179077
|
-
if (path !== undefined) {
|
|
179078
|
-
return path;
|
|
179079
|
-
}
|
|
179080
|
-
}
|
|
179081
|
-
if (typeof record?.rootUri === "string") {
|
|
179082
|
-
const path = uriToPath(record.rootUri);
|
|
179083
|
-
if (path !== undefined) {
|
|
179084
|
-
return path;
|
|
179085
|
-
}
|
|
179086
|
-
}
|
|
179087
|
-
if (typeof record?.rootPath === "string") {
|
|
179088
|
-
return record.rootPath;
|
|
179089
|
-
}
|
|
179090
|
-
return process.cwd();
|
|
179091
|
-
}
|
|
179092
|
-
function runLspServer() {
|
|
179093
|
-
const reader = new MessageReader;
|
|
179094
|
-
const server2 = new Server((message) => {
|
|
179095
|
-
process.stdout.write(encodeMessage(message));
|
|
179096
|
-
});
|
|
179097
|
-
process.stdin.on("data", (chunk) => {
|
|
179098
|
-
reader.append(chunk);
|
|
179099
|
-
for (const message of reader.read()) {
|
|
179100
|
-
server2.handle(message);
|
|
179101
|
-
}
|
|
179102
|
-
});
|
|
179103
|
-
process.stdin.resume();
|
|
179104
|
-
}
|
|
179105
|
-
function uriToPath(uri) {
|
|
179106
|
-
try {
|
|
179107
|
-
return fileURLToPath3(uri);
|
|
179108
|
-
} catch {
|
|
179109
|
-
return;
|
|
179110
|
-
}
|
|
179111
|
-
}
|
|
179112
|
-
function toLocation(root, locator) {
|
|
179113
|
-
return {
|
|
179114
|
-
uri: relativePathToUri(root, locator.filePath),
|
|
179115
|
-
range: toLspRange(locator.range)
|
|
179116
|
-
};
|
|
179117
|
-
}
|
|
179118
|
-
function textDocument(params) {
|
|
179119
|
-
const record = params;
|
|
179120
|
-
const doc = record?.textDocument;
|
|
179121
|
-
if (doc === undefined) {
|
|
179122
|
-
return;
|
|
179123
|
-
}
|
|
179124
|
-
const result = {};
|
|
179125
|
-
if (typeof doc.uri === "string") {
|
|
179126
|
-
result.uri = doc.uri;
|
|
179127
|
-
}
|
|
179128
|
-
if (typeof doc.text === "string") {
|
|
179129
|
-
result.text = doc.text;
|
|
179130
|
-
}
|
|
179131
|
-
return result;
|
|
179132
|
-
}
|
|
179133
|
-
function fullChangeText(changes) {
|
|
179134
|
-
if (!Array.isArray(changes) || changes.length === 0) {
|
|
179135
|
-
return;
|
|
179136
|
-
}
|
|
179137
|
-
const last = changes[changes.length - 1];
|
|
179138
|
-
return typeof last.text === "string" ? last.text : undefined;
|
|
179139
|
-
}
|
|
179140
|
-
|
|
179141
|
-
// src/cli/docs.ts
|
|
179142
|
-
import { readdirSync as readdirSync3, readFileSync as readFileSync4 } from "node:fs";
|
|
179143
|
-
import { join as join6 } from "node:path";
|
|
179144
|
-
|
|
179145
|
-
// src/core/init-plan.ts
|
|
179146
|
-
import { existsSync as existsSync2, lstatSync as lstatSync2, readdirSync as readdirSync2, readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
|
|
179147
|
-
import { dirname as dirname3, join as join5, relative as relative3 } from "node:path";
|
|
179148
|
-
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
179149
|
-
var CONFIG_FILE_NAME2 = "docbridge.config.json";
|
|
179150
|
-
var INIT_SKILL_NAMES = ["docbridge"];
|
|
179151
|
-
var LEGACY_SKILL_NAMES = [
|
|
179152
|
-
"docbridge-adopt",
|
|
179153
|
-
"docbridge-annotate",
|
|
179154
|
-
"docbridge-link",
|
|
179155
|
-
"docbridge-review",
|
|
179156
|
-
"docbridge-sync"
|
|
179157
|
-
];
|
|
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
|
-
projectRoot: input.projectRoot,
|
|
179202
|
-
packageRoot,
|
|
179203
|
-
agentTarget,
|
|
179204
|
-
options: input.options,
|
|
179205
|
-
messages
|
|
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 the docbridge skill 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
|
-
};
|
|
179236
|
-
}
|
|
179237
|
-
function resolvePackageRoot(moduleUrl = import.meta.url) {
|
|
179238
|
-
const modulePath = fileURLToPath4(moduleUrl);
|
|
179239
|
-
let resolved;
|
|
179240
|
-
try {
|
|
179241
|
-
resolved = realpathSync2(modulePath);
|
|
179242
|
-
} catch {
|
|
179243
|
-
resolved = modulePath;
|
|
179780
|
+
this.clearTimer();
|
|
179781
|
+
this.timer = setTimeout(() => {
|
|
179782
|
+
this.timer = null;
|
|
179783
|
+
this.flush();
|
|
179784
|
+
}, this.debounceMs);
|
|
179244
179785
|
}
|
|
179245
|
-
|
|
179246
|
-
|
|
179247
|
-
if (
|
|
179248
|
-
return
|
|
179786
|
+
flush() {
|
|
179787
|
+
this.clearTimer();
|
|
179788
|
+
if (this.project === null || !this.dirty) {
|
|
179789
|
+
return;
|
|
179249
179790
|
}
|
|
179250
|
-
|
|
179251
|
-
|
|
179252
|
-
|
|
179791
|
+
this.dirty = false;
|
|
179792
|
+
const state = this.project.resolve();
|
|
179793
|
+
for (const rel of this.openFiles) {
|
|
179794
|
+
const content = state.contentByFile.get(rel) ?? "";
|
|
179795
|
+
this.publish(rel, diagnosticsForFile(state.diagnostics, rel, content));
|
|
179253
179796
|
}
|
|
179254
|
-
dir = parent;
|
|
179255
|
-
}
|
|
179256
|
-
}
|
|
179257
|
-
function listDistributableSkills(packageRoot) {
|
|
179258
|
-
const skillsRoot = join5(packageRoot, "templates", "skills");
|
|
179259
|
-
let entries;
|
|
179260
|
-
try {
|
|
179261
|
-
entries = readdirSync2(skillsRoot);
|
|
179262
|
-
} catch {
|
|
179263
|
-
return [];
|
|
179264
|
-
}
|
|
179265
|
-
return entries.filter((entry) => entry.startsWith("docbridge")).filter((entry) => existsSync2(join5(skillsRoot, entry, "SKILL.md"))).toSorted();
|
|
179266
|
-
}
|
|
179267
|
-
function formatInitPlan(plan) {
|
|
179268
|
-
const lines = [];
|
|
179269
|
-
for (const message of plan.messages) {
|
|
179270
|
-
lines.push(message);
|
|
179271
179797
|
}
|
|
179272
|
-
|
|
179273
|
-
|
|
179274
|
-
|
|
179275
|
-
for (const operation of plan.configOps) {
|
|
179276
|
-
lines.push(formatFileOp(operation));
|
|
179277
|
-
if (operation.content !== undefined) {
|
|
179278
|
-
lines.push(operation.content);
|
|
179279
|
-
}
|
|
179798
|
+
publish(rel, diagnostics) {
|
|
179799
|
+
if (this.project === null) {
|
|
179800
|
+
return;
|
|
179280
179801
|
}
|
|
179802
|
+
this.send({
|
|
179803
|
+
jsonrpc: "2.0",
|
|
179804
|
+
method: "textDocument/publishDiagnostics",
|
|
179805
|
+
params: { uri: relativePathToUri(this.project.root, rel), diagnostics }
|
|
179806
|
+
});
|
|
179281
179807
|
}
|
|
179282
|
-
|
|
179283
|
-
|
|
179284
|
-
|
|
179285
|
-
for (const operation of plan.skillOps) {
|
|
179286
|
-
lines.push(formatFileOp(operation));
|
|
179808
|
+
relPath(uri) {
|
|
179809
|
+
if (uri === undefined || this.project === null) {
|
|
179810
|
+
return;
|
|
179287
179811
|
}
|
|
179812
|
+
return uriToRelativePath(this.project.root, uri);
|
|
179288
179813
|
}
|
|
179289
|
-
|
|
179290
|
-
|
|
179291
|
-
|
|
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);
|
|
179814
|
+
respond(id, result) {
|
|
179815
|
+
if (id === undefined) {
|
|
179816
|
+
return;
|
|
179298
179817
|
}
|
|
179818
|
+
this.send({ jsonrpc: "2.0", id, result });
|
|
179299
179819
|
}
|
|
179300
|
-
|
|
179301
|
-
|
|
179302
|
-
|
|
179303
|
-
|
|
179304
|
-
lines.push(`- ${step}`);
|
|
179820
|
+
clearTimer() {
|
|
179821
|
+
if (this.timer !== null) {
|
|
179822
|
+
clearTimeout(this.timer);
|
|
179823
|
+
this.timer = null;
|
|
179305
179824
|
}
|
|
179306
179825
|
}
|
|
179307
|
-
return `${lines.join(`
|
|
179308
|
-
`)}
|
|
179309
|
-
`;
|
|
179310
179826
|
}
|
|
179311
|
-
function
|
|
179312
|
-
|
|
179313
|
-
|
|
179314
|
-
|
|
179315
|
-
|
|
179316
|
-
|
|
179317
|
-
|
|
179318
|
-
input.messages.push(input.discovery.docs.message);
|
|
179319
|
-
}
|
|
179320
|
-
if (input.discovery.code.message !== undefined) {
|
|
179321
|
-
input.messages.push(input.discovery.code.message);
|
|
179827
|
+
function resolveRoot(params) {
|
|
179828
|
+
const record = params;
|
|
179829
|
+
const folderUri = record?.workspaceFolders?.[0]?.uri;
|
|
179830
|
+
if (typeof folderUri === "string") {
|
|
179831
|
+
const path = uriToPath(folderUri);
|
|
179832
|
+
if (path !== undefined) {
|
|
179833
|
+
return path;
|
|
179322
179834
|
}
|
|
179323
|
-
|
|
179324
|
-
|
|
179835
|
+
}
|
|
179836
|
+
if (typeof record?.rootUri === "string") {
|
|
179837
|
+
const path = uriToPath(record.rootUri);
|
|
179838
|
+
if (path !== undefined) {
|
|
179839
|
+
return path;
|
|
179325
179840
|
}
|
|
179326
|
-
return [];
|
|
179327
179841
|
}
|
|
179328
|
-
|
|
179329
|
-
|
|
179330
|
-
|
|
179331
|
-
|
|
179332
|
-
|
|
179333
|
-
|
|
179334
|
-
|
|
179335
|
-
|
|
179336
|
-
|
|
179337
|
-
|
|
179338
|
-
|
|
179339
|
-
|
|
179842
|
+
if (typeof record?.rootPath === "string") {
|
|
179843
|
+
return record.rootPath;
|
|
179844
|
+
}
|
|
179845
|
+
return process.cwd();
|
|
179846
|
+
}
|
|
179847
|
+
function runLspServer() {
|
|
179848
|
+
const reader = new MessageReader;
|
|
179849
|
+
const server2 = new Server((message) => {
|
|
179850
|
+
process.stdout.write(encodeMessage(message));
|
|
179851
|
+
});
|
|
179852
|
+
process.stdin.on("data", (chunk) => {
|
|
179853
|
+
reader.append(chunk);
|
|
179854
|
+
for (const message of reader.read()) {
|
|
179855
|
+
server2.handle(message);
|
|
179340
179856
|
}
|
|
179341
|
-
|
|
179857
|
+
});
|
|
179858
|
+
process.stdin.resume();
|
|
179342
179859
|
}
|
|
179343
|
-
function
|
|
179344
|
-
|
|
179860
|
+
function uriToPath(uri) {
|
|
179861
|
+
try {
|
|
179862
|
+
return fileURLToPath4(uri);
|
|
179863
|
+
} catch {
|
|
179345
179864
|
return;
|
|
179346
179865
|
}
|
|
179347
|
-
|
|
179866
|
+
}
|
|
179867
|
+
function toLocation(root, locator) {
|
|
179868
|
+
return {
|
|
179869
|
+
uri: relativePathToUri(root, locator.filePath),
|
|
179870
|
+
range: toLspRange(locator.range)
|
|
179871
|
+
};
|
|
179872
|
+
}
|
|
179873
|
+
function textDocument(params) {
|
|
179874
|
+
const record = params;
|
|
179875
|
+
const doc = record?.textDocument;
|
|
179876
|
+
if (doc === undefined) {
|
|
179348
179877
|
return;
|
|
179349
179878
|
}
|
|
179350
|
-
|
|
179879
|
+
const result = {};
|
|
179880
|
+
if (typeof doc.uri === "string") {
|
|
179881
|
+
result.uri = doc.uri;
|
|
179882
|
+
}
|
|
179883
|
+
if (typeof doc.text === "string") {
|
|
179884
|
+
result.text = doc.text;
|
|
179885
|
+
}
|
|
179886
|
+
return result;
|
|
179887
|
+
}
|
|
179888
|
+
function fullChangeText(changes) {
|
|
179889
|
+
if (!Array.isArray(changes) || changes.length === 0) {
|
|
179351
179890
|
return;
|
|
179352
179891
|
}
|
|
179353
|
-
|
|
179354
|
-
|
|
179355
|
-
languages: discovery.code.languages
|
|
179356
|
-
};
|
|
179892
|
+
const last = changes[changes.length - 1];
|
|
179893
|
+
return typeof last.text === "string" ? last.text : undefined;
|
|
179357
179894
|
}
|
|
179358
|
-
|
|
179359
|
-
|
|
179360
|
-
|
|
179895
|
+
|
|
179896
|
+
// src/setup/update-cache.ts
|
|
179897
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync } from "node:fs";
|
|
179898
|
+
import { homedir } from "node:os";
|
|
179899
|
+
import { dirname as dirname4, join as join7 } from "node:path";
|
|
179900
|
+
var UPDATE_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
179901
|
+
var UPDATE_CACHE_SCHEMA_VERSION = 1;
|
|
179902
|
+
function resolveUpdateCachePath(env = process.env, home = homedir()) {
|
|
179903
|
+
const explicit = env.DOCBRIDGE_UPDATE_CACHE;
|
|
179904
|
+
if (explicit !== undefined && explicit.length > 0) {
|
|
179905
|
+
return explicit;
|
|
179906
|
+
}
|
|
179907
|
+
const cacheRoot = env.XDG_CACHE_HOME !== undefined && env.XDG_CACHE_HOME.length > 0 ? env.XDG_CACHE_HOME : join7(home, ".cache");
|
|
179908
|
+
return join7(cacheRoot, "docbridge", "update-check.json");
|
|
179909
|
+
}
|
|
179910
|
+
function readUpdateCache(path) {
|
|
179911
|
+
let rawText;
|
|
179912
|
+
try {
|
|
179913
|
+
rawText = readFileSync4(path, "utf8");
|
|
179914
|
+
} catch {
|
|
179915
|
+
return;
|
|
179361
179916
|
}
|
|
179362
|
-
|
|
179363
|
-
|
|
179364
|
-
|
|
179365
|
-
|
|
179366
|
-
|
|
179367
|
-
if (!availableSkills.has(skillName)) {
|
|
179368
|
-
continue;
|
|
179369
|
-
}
|
|
179370
|
-
const destinationDir = join5(input.projectRoot, destination, skillName);
|
|
179371
|
-
const path = relative3(input.projectRoot, destinationDir);
|
|
179372
|
-
if (isSymlink(destinationDir)) {
|
|
179373
|
-
input.messages.push(`Skill directory ${path} is a symlink and was left in place.`);
|
|
179374
|
-
continue;
|
|
179375
|
-
}
|
|
179376
|
-
const exists = existsSync2(destinationDir);
|
|
179377
|
-
let action;
|
|
179378
|
-
let reason;
|
|
179379
|
-
if (exists && input.options.force) {
|
|
179380
|
-
action = input.options.dryRun ? "would-overwrite" : "overwrite";
|
|
179381
|
-
reason = "Overwrite existing skill directory.";
|
|
179382
|
-
} else if (exists) {
|
|
179383
|
-
action = "skip";
|
|
179384
|
-
reason = "Skill directory already exists.";
|
|
179385
|
-
} else {
|
|
179386
|
-
action = input.options.dryRun ? "would-create" : "create";
|
|
179387
|
-
reason = "Install distributable skill template.";
|
|
179388
|
-
}
|
|
179389
|
-
operations.push({
|
|
179390
|
-
action,
|
|
179391
|
-
path,
|
|
179392
|
-
reason
|
|
179393
|
-
});
|
|
179394
|
-
}
|
|
179395
|
-
operations.push(...planLegacySkillOperations({
|
|
179396
|
-
projectRoot: input.projectRoot,
|
|
179397
|
-
destination,
|
|
179398
|
-
options: input.options,
|
|
179399
|
-
messages: input.messages
|
|
179400
|
-
}));
|
|
179917
|
+
let parsed;
|
|
179918
|
+
try {
|
|
179919
|
+
parsed = JSON.parse(rawText);
|
|
179920
|
+
} catch {
|
|
179921
|
+
return;
|
|
179401
179922
|
}
|
|
179402
|
-
return
|
|
179923
|
+
return toUpdateCacheRecord(parsed);
|
|
179403
179924
|
}
|
|
179404
|
-
function
|
|
179405
|
-
|
|
179406
|
-
|
|
179407
|
-
|
|
179408
|
-
|
|
179409
|
-
|
|
179410
|
-
|
|
179411
|
-
|
|
179412
|
-
|
|
179413
|
-
|
|
179414
|
-
|
|
179415
|
-
|
|
179416
|
-
|
|
179417
|
-
|
|
179418
|
-
operations.push({
|
|
179419
|
-
action: input.options.dryRun ? "would-remove" : "remove",
|
|
179420
|
-
path,
|
|
179421
|
-
reason: "Remove leftover skill from the previous five-skill layout."
|
|
179422
|
-
});
|
|
179423
|
-
} else {
|
|
179424
|
-
leftover.push(path);
|
|
179425
|
-
}
|
|
179925
|
+
function writeUpdateCache(path, record) {
|
|
179926
|
+
try {
|
|
179927
|
+
mkdirSync2(dirname4(path), { recursive: true });
|
|
179928
|
+
writeFileSync(path, `${JSON.stringify(record, null, 2)}
|
|
179929
|
+
`, "utf8");
|
|
179930
|
+
} catch {}
|
|
179931
|
+
}
|
|
179932
|
+
function isUpdateCacheFresh(record, now, ttlMs = UPDATE_CACHE_TTL_MS) {
|
|
179933
|
+
const age = now - record.checkedAt;
|
|
179934
|
+
return age >= 0 && age < ttlMs;
|
|
179935
|
+
}
|
|
179936
|
+
function toUpdateCacheRecord(value) {
|
|
179937
|
+
if (typeof value !== "object" || value === null) {
|
|
179938
|
+
return;
|
|
179426
179939
|
}
|
|
179427
|
-
|
|
179428
|
-
|
|
179940
|
+
const candidate = value;
|
|
179941
|
+
if (candidate.schema !== UPDATE_CACHE_SCHEMA_VERSION) {
|
|
179942
|
+
return;
|
|
179429
179943
|
}
|
|
179430
|
-
|
|
179944
|
+
if (typeof candidate.checkedAt !== "number" || !Number.isFinite(candidate.checkedAt)) {
|
|
179945
|
+
return;
|
|
179946
|
+
}
|
|
179947
|
+
if (candidate.latest !== null && typeof candidate.latest !== "string") {
|
|
179948
|
+
return;
|
|
179949
|
+
}
|
|
179950
|
+
return {
|
|
179951
|
+
schema: UPDATE_CACHE_SCHEMA_VERSION,
|
|
179952
|
+
checkedAt: candidate.checkedAt,
|
|
179953
|
+
latest: candidate.latest
|
|
179954
|
+
};
|
|
179431
179955
|
}
|
|
179432
|
-
|
|
179433
|
-
|
|
179434
|
-
|
|
179435
|
-
|
|
179436
|
-
|
|
179437
|
-
|
|
179438
|
-
|
|
179439
|
-
fallbackPrompt: buildFallbackPrompt(projectRoot, "codex")
|
|
179440
|
-
});
|
|
179956
|
+
|
|
179957
|
+
// src/setup/version.ts
|
|
179958
|
+
var STABLE_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
|
|
179959
|
+
function parseStableVersion(value) {
|
|
179960
|
+
const match = STABLE_VERSION_PATTERN.exec(value.trim());
|
|
179961
|
+
if (match === null) {
|
|
179962
|
+
return;
|
|
179441
179963
|
}
|
|
179442
|
-
|
|
179443
|
-
|
|
179444
|
-
|
|
179445
|
-
destination: ".claude/skills/docbridge/",
|
|
179446
|
-
oneShotCommand: `cd ${projectRoot} && /docbridge adopt DocBridge in this repository`,
|
|
179447
|
-
fallbackPrompt: buildFallbackPrompt(projectRoot, "claude")
|
|
179448
|
-
});
|
|
179964
|
+
const [, major, minor, patch] = match;
|
|
179965
|
+
if (major === undefined || minor === undefined || patch === undefined) {
|
|
179966
|
+
return;
|
|
179449
179967
|
}
|
|
179450
|
-
return
|
|
179968
|
+
return {
|
|
179969
|
+
major: Number.parseInt(major, 10),
|
|
179970
|
+
minor: Number.parseInt(minor, 10),
|
|
179971
|
+
patch: Number.parseInt(patch, 10)
|
|
179972
|
+
};
|
|
179451
179973
|
}
|
|
179452
|
-
function
|
|
179453
|
-
|
|
179454
|
-
return `${invocation} to adopt DocBridge in ${projectRoot}. Confirm docs and code scope, create or improve docbridge.config.json, and suggest the next linking steps.`;
|
|
179974
|
+
function compareStableVersions(left, right) {
|
|
179975
|
+
return left.major - right.major || left.minor - right.minor || left.patch - right.patch;
|
|
179455
179976
|
}
|
|
179456
|
-
function
|
|
179457
|
-
|
|
179458
|
-
|
|
179459
|
-
|
|
179460
|
-
|
|
179461
|
-
return { target: discovery.agent.defaultTarget, error: undefined };
|
|
179977
|
+
function isNewerStableVersion(candidate, current) {
|
|
179978
|
+
const parsedCandidate = parseStableVersion(candidate);
|
|
179979
|
+
const parsedCurrent = parseStableVersion(current);
|
|
179980
|
+
if (parsedCandidate === undefined || parsedCurrent === undefined) {
|
|
179981
|
+
return false;
|
|
179462
179982
|
}
|
|
179463
|
-
return
|
|
179983
|
+
return compareStableVersions(parsedCandidate, parsedCurrent) > 0;
|
|
179464
179984
|
}
|
|
179465
|
-
|
|
179466
|
-
|
|
179467
|
-
|
|
179468
|
-
|
|
179469
|
-
|
|
179470
|
-
|
|
179471
|
-
|
|
179472
|
-
}
|
|
179473
|
-
return { target: options.agentTarget, error: undefined };
|
|
179985
|
+
|
|
179986
|
+
// src/setup/registry.ts
|
|
179987
|
+
var NPM_REGISTRY_LATEST_URL = "https://registry.npmjs.org/docbridge/latest";
|
|
179988
|
+
var REGISTRY_TIMEOUT_MS = 1500;
|
|
179989
|
+
function selectStableVersion(payload) {
|
|
179990
|
+
if (typeof payload !== "object" || payload === null) {
|
|
179991
|
+
return;
|
|
179474
179992
|
}
|
|
179475
|
-
|
|
179476
|
-
|
|
179477
|
-
|
|
179478
|
-
target: undefined,
|
|
179479
|
-
error: "init-with-agent --yes requires an explicit agent target when no .agents/ or .claude/ directory exists. Pass --agent-target codex, claude, or both."
|
|
179480
|
-
};
|
|
179481
|
-
}
|
|
179482
|
-
return { target: discovery.agent.recommendedTarget, error: undefined };
|
|
179993
|
+
const version = payload.version;
|
|
179994
|
+
if (typeof version !== "string") {
|
|
179995
|
+
return;
|
|
179483
179996
|
}
|
|
179484
|
-
return
|
|
179997
|
+
return parseStableVersion(version) === undefined ? undefined : version.trim();
|
|
179485
179998
|
}
|
|
179486
|
-
function
|
|
179487
|
-
const
|
|
179488
|
-
|
|
179999
|
+
function resolveRegistryUrl(env = process.env) {
|
|
180000
|
+
const override = env.DOCBRIDGE_REGISTRY_URL;
|
|
180001
|
+
return override !== undefined && override.length > 0 ? override : NPM_REGISTRY_LATEST_URL;
|
|
180002
|
+
}
|
|
180003
|
+
async function fetchLatestStableVersion(options = {}) {
|
|
180004
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
180005
|
+
const url = options.url ?? resolveRegistryUrl();
|
|
179489
180006
|
try {
|
|
179490
|
-
|
|
180007
|
+
const response = await fetchImpl(url, {
|
|
180008
|
+
signal: AbortSignal.timeout(options.timeoutMs ?? REGISTRY_TIMEOUT_MS),
|
|
180009
|
+
headers: { accept: "application/json" }
|
|
180010
|
+
});
|
|
180011
|
+
if (!response.ok) {
|
|
180012
|
+
return;
|
|
180013
|
+
}
|
|
180014
|
+
return selectStableVersion(await response.json());
|
|
179491
180015
|
} catch {
|
|
179492
|
-
|
|
180016
|
+
return;
|
|
179493
180017
|
}
|
|
179494
|
-
|
|
180018
|
+
}
|
|
180019
|
+
async function resolveLatestStableVersion(options = {}) {
|
|
180020
|
+
const now = options.now ?? Date.now();
|
|
180021
|
+
const cachePath = options.cachePath ?? resolveUpdateCachePath();
|
|
180022
|
+
if (options.forceRefresh !== true) {
|
|
180023
|
+
const cached = readUpdateCache(cachePath);
|
|
180024
|
+
if (cached !== undefined && isUpdateCacheFresh(cached, now)) {
|
|
180025
|
+
return cached.latest === null ? { status: "unavailable", source: "cache" } : { status: "ok", latest: cached.latest, source: "cache" };
|
|
180026
|
+
}
|
|
180027
|
+
}
|
|
180028
|
+
const fetched = await fetchLatestStableVersion({
|
|
180029
|
+
...options.url !== undefined ? { url: options.url } : {},
|
|
180030
|
+
...options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {},
|
|
180031
|
+
...options.fetchImpl !== undefined ? { fetchImpl: options.fetchImpl } : {}
|
|
180032
|
+
});
|
|
180033
|
+
writeUpdateCache(cachePath, {
|
|
180034
|
+
schema: UPDATE_CACHE_SCHEMA_VERSION,
|
|
180035
|
+
checkedAt: now,
|
|
180036
|
+
latest: fetched ?? null
|
|
180037
|
+
});
|
|
180038
|
+
return fetched === undefined ? { status: "unavailable", source: "network" } : { status: "ok", latest: fetched, source: "network" };
|
|
180039
|
+
}
|
|
180040
|
+
|
|
180041
|
+
// src/setup/upgrade-guidance.ts
|
|
180042
|
+
import { sep as sep2 } from "node:path";
|
|
180043
|
+
var PACKAGE_MANAGERS = ["bun", "npm", "pnpm", "yarn"];
|
|
180044
|
+
var PROJECT_COMMANDS = {
|
|
180045
|
+
bun: "bun add -d docbridge@latest",
|
|
180046
|
+
npm: "npm install --save-dev docbridge@latest",
|
|
180047
|
+
pnpm: "pnpm add -D docbridge@latest",
|
|
180048
|
+
yarn: "yarn add -D docbridge@latest"
|
|
180049
|
+
};
|
|
180050
|
+
var GLOBAL_COMMANDS = {
|
|
180051
|
+
bun: "bun add -g docbridge@latest",
|
|
180052
|
+
npm: "npm install -g docbridge@latest",
|
|
180053
|
+
pnpm: "pnpm add -g docbridge@latest",
|
|
180054
|
+
yarn: "yarn global add docbridge@latest"
|
|
180055
|
+
};
|
|
180056
|
+
function detectPackageManager(env = process.env) {
|
|
180057
|
+
const userAgent = env.npm_config_user_agent;
|
|
180058
|
+
if (userAgent === undefined || userAgent.length === 0) {
|
|
180059
|
+
return;
|
|
180060
|
+
}
|
|
180061
|
+
const name = userAgent.trim().split("/", 1)[0]?.toLowerCase();
|
|
180062
|
+
return PACKAGE_MANAGERS.find((manager) => manager === name);
|
|
180063
|
+
}
|
|
180064
|
+
function detectInstallScope(packageRoot, candidateRoots) {
|
|
180065
|
+
const installBase = resolveInstallBase(packageRoot);
|
|
180066
|
+
if (installBase === undefined) {
|
|
180067
|
+
return "unknown";
|
|
180068
|
+
}
|
|
180069
|
+
return candidateRoots.some((root) => isSameOrAncestor(installBase, root)) ? "project" : "global";
|
|
180070
|
+
}
|
|
180071
|
+
function detectUpgradeGuidance(input) {
|
|
180072
|
+
const packageManager = detectPackageManager(input.env ?? process.env);
|
|
180073
|
+
const scope = detectInstallScope(input.packageRoot, [
|
|
180074
|
+
input.projectRoot,
|
|
180075
|
+
input.currentDirectory ?? process.cwd()
|
|
180076
|
+
]);
|
|
180077
|
+
const commands = scope === "global" ? GLOBAL_COMMANDS : PROJECT_COMMANDS;
|
|
180078
|
+
const primaryManager = packageManager ?? "npm";
|
|
179495
180079
|
return {
|
|
179496
|
-
|
|
179497
|
-
|
|
179498
|
-
|
|
179499
|
-
|
|
180080
|
+
packageManager,
|
|
180081
|
+
scope,
|
|
180082
|
+
primaryCommand: commands[primaryManager],
|
|
180083
|
+
alternativeCommands: PACKAGE_MANAGERS.filter((manager) => manager !== primaryManager).map((manager) => commands[manager])
|
|
179500
180084
|
};
|
|
179501
180085
|
}
|
|
179502
|
-
function
|
|
179503
|
-
|
|
179504
|
-
|
|
179505
|
-
|
|
180086
|
+
function formatUpgradeGuidance(guidance) {
|
|
180087
|
+
const scopeLabel = guidance.scope === "unknown" ? "install scope not detected" : `${guidance.scope} install`;
|
|
180088
|
+
return [
|
|
180089
|
+
`Upgrade command (${scopeLabel}): ${guidance.primaryCommand}`,
|
|
180090
|
+
`Other package managers: ${guidance.alternativeCommands.join(", ")}`
|
|
180091
|
+
];
|
|
179506
180092
|
}
|
|
179507
|
-
function
|
|
179508
|
-
|
|
179509
|
-
|
|
179510
|
-
|
|
179511
|
-
case "claude":
|
|
179512
|
-
return [".claude/skills"];
|
|
179513
|
-
case "both":
|
|
179514
|
-
return [".agents/skills", ".claude/skills"];
|
|
179515
|
-
default:
|
|
179516
|
-
return [];
|
|
179517
|
-
}
|
|
180093
|
+
function resolveInstallBase(packageRoot) {
|
|
180094
|
+
const marker = `${sep2}node_modules${sep2}`;
|
|
180095
|
+
const markerIndex = withTrailingSeparator(packageRoot).indexOf(marker);
|
|
180096
|
+
return markerIndex === -1 ? undefined : packageRoot.slice(0, markerIndex);
|
|
179518
180097
|
}
|
|
179519
|
-
function
|
|
179520
|
-
|
|
179521
|
-
|
|
179522
|
-
|
|
179523
|
-
|
|
180098
|
+
function isSameOrAncestor(candidate, descendant) {
|
|
180099
|
+
return withTrailingSeparator(descendant).startsWith(withTrailingSeparator(candidate));
|
|
180100
|
+
}
|
|
180101
|
+
function withTrailingSeparator(path) {
|
|
180102
|
+
return path.endsWith(sep2) ? path : `${path}${sep2}`;
|
|
180103
|
+
}
|
|
180104
|
+
|
|
180105
|
+
// src/setup/update-notice.ts
|
|
180106
|
+
var SUPPRESSED_COMMANDS = new Set(["lsp", "upgrade"]);
|
|
180107
|
+
function isUpdateCheckOptedOut(env = process.env) {
|
|
180108
|
+
return isEnabledFlag(env.DOCBRIDGE_NO_UPDATE_CHECK);
|
|
180109
|
+
}
|
|
180110
|
+
function decideUpdateCheck(input) {
|
|
180111
|
+
const env = input.env ?? process.env;
|
|
180112
|
+
if (isUpdateCheckOptedOut(env)) {
|
|
180113
|
+
return { enabled: false, reason: "opt-out" };
|
|
180114
|
+
}
|
|
180115
|
+
if (isEnabledFlag(env.CI)) {
|
|
180116
|
+
return { enabled: false, reason: "ci" };
|
|
180117
|
+
}
|
|
180118
|
+
if (!input.isTty) {
|
|
180119
|
+
return { enabled: false, reason: "non-tty" };
|
|
179524
180120
|
}
|
|
180121
|
+
if (input.argv.includes("--json")) {
|
|
180122
|
+
return { enabled: false, reason: "json-output" };
|
|
180123
|
+
}
|
|
180124
|
+
const command = input.argv[0];
|
|
180125
|
+
if (command !== undefined && SUPPRESSED_COMMANDS.has(command)) {
|
|
180126
|
+
return { enabled: false, reason: "machine-command" };
|
|
180127
|
+
}
|
|
180128
|
+
return { enabled: true };
|
|
179525
180129
|
}
|
|
179526
|
-
function
|
|
179527
|
-
|
|
179528
|
-
|
|
179529
|
-
label = "would create";
|
|
179530
|
-
} else if (operation.action === "would-overwrite") {
|
|
179531
|
-
label = "would overwrite";
|
|
179532
|
-
} else if (operation.action === "would-remove") {
|
|
179533
|
-
label = "would remove";
|
|
180130
|
+
function formatUpdateNotice(input) {
|
|
180131
|
+
if (!isNewerStableVersion(input.latest, input.current)) {
|
|
180132
|
+
return;
|
|
179534
180133
|
}
|
|
179535
|
-
|
|
179536
|
-
|
|
180134
|
+
return [
|
|
180135
|
+
`Update available: docbridge ${input.current} -> ${input.latest}`,
|
|
180136
|
+
formatUpgradeGuidance(input.guidance)[0] ?? "",
|
|
180137
|
+
"Run `docbridge upgrade --check` for details, or set DOCBRIDGE_NO_UPDATE_CHECK=1 to silence this notice.",
|
|
180138
|
+
""
|
|
180139
|
+
].join(`
|
|
180140
|
+
`);
|
|
179537
180141
|
}
|
|
179538
|
-
function
|
|
179539
|
-
|
|
180142
|
+
function isEnabledFlag(value) {
|
|
180143
|
+
if (value === undefined) {
|
|
180144
|
+
return false;
|
|
180145
|
+
}
|
|
180146
|
+
const normalized = value.trim().toLowerCase();
|
|
180147
|
+
return normalized.length > 0 && normalized !== "0" && normalized !== "false";
|
|
179540
180148
|
}
|
|
179541
180149
|
|
|
180150
|
+
// src/cli/docs.ts
|
|
180151
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync5 } from "node:fs";
|
|
180152
|
+
import { join as join8 } from "node:path";
|
|
180153
|
+
|
|
179542
180154
|
// src/cli/errors.ts
|
|
179543
180155
|
class CliError extends Error {
|
|
179544
180156
|
guidance;
|
|
@@ -179599,6 +180211,19 @@ function agentTargetGuidance(command) {
|
|
|
179599
180211
|
].join(`
|
|
179600
180212
|
`);
|
|
179601
180213
|
}
|
|
180214
|
+
function upgradeConfirmationGuidance() {
|
|
180215
|
+
return [
|
|
180216
|
+
"Confirm the migration explicitly:",
|
|
180217
|
+
"",
|
|
180218
|
+
" docbridge upgrade --force --yes",
|
|
180219
|
+
"",
|
|
180220
|
+
"Or inspect it first:",
|
|
180221
|
+
"",
|
|
180222
|
+
" docbridge upgrade --check",
|
|
180223
|
+
" docbridge upgrade --force --dry-run"
|
|
180224
|
+
].join(`
|
|
180225
|
+
`);
|
|
180226
|
+
}
|
|
179602
180227
|
function includeContentGuidance() {
|
|
179603
180228
|
return [
|
|
179604
180229
|
"Use --include-content together with --json:",
|
|
@@ -179625,6 +180250,9 @@ function configSetupGuidance() {
|
|
|
179625
180250
|
function configRepairGuidance() {
|
|
179626
180251
|
return "Repair or delete docbridge.config.json, then re-run `docbridge check`.";
|
|
179627
180252
|
}
|
|
180253
|
+
function manifestRepairGuidance() {
|
|
180254
|
+
return "Repair or delete docbridge.links.json, then re-run `docbridge check`.";
|
|
180255
|
+
}
|
|
179628
180256
|
function formatCliError(error) {
|
|
179629
180257
|
if (!(error instanceof CliError)) {
|
|
179630
180258
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -179692,11 +180320,11 @@ function parseDocument(name, source) {
|
|
|
179692
180320
|
};
|
|
179693
180321
|
}
|
|
179694
180322
|
function createFileDocumentationReader(packageRoot) {
|
|
179695
|
-
const docsRoot =
|
|
180323
|
+
const docsRoot = join8(packageRoot, "docs", "user");
|
|
179696
180324
|
function documents() {
|
|
179697
180325
|
let fileNames;
|
|
179698
180326
|
try {
|
|
179699
|
-
fileNames =
|
|
180327
|
+
fileNames = readdirSync2(docsRoot).filter((fileName) => fileName.endsWith(".md")).toSorted();
|
|
179700
180328
|
} catch {
|
|
179701
180329
|
throw documentationUnavailableError();
|
|
179702
180330
|
}
|
|
@@ -179705,7 +180333,7 @@ function createFileDocumentationReader(packageRoot) {
|
|
|
179705
180333
|
}
|
|
179706
180334
|
return fileNames.map((fileName) => {
|
|
179707
180335
|
const name = fileName.slice(0, -".md".length);
|
|
179708
|
-
return parseDocument(name,
|
|
180336
|
+
return parseDocument(name, readFileSync5(join8(docsRoot, fileName), "utf8"));
|
|
179709
180337
|
});
|
|
179710
180338
|
}
|
|
179711
180339
|
return {
|
|
@@ -179776,6 +180404,7 @@ var SUBCOMMANDS = [
|
|
|
179776
180404
|
"docs",
|
|
179777
180405
|
"init",
|
|
179778
180406
|
"init-with-agent",
|
|
180407
|
+
"upgrade",
|
|
179779
180408
|
"lsp"
|
|
179780
180409
|
];
|
|
179781
180410
|
var ROOT_OPTION = {
|
|
@@ -179901,6 +180530,42 @@ var COMMAND_DOCS = {
|
|
|
179901
180530
|
`),
|
|
179902
180531
|
options: [JSON_OPTION]
|
|
179903
180532
|
},
|
|
180533
|
+
upgrade: {
|
|
180534
|
+
usage: "[options]",
|
|
180535
|
+
summary: "Use to check for a newer release and migrate managed skills.",
|
|
180536
|
+
description: [
|
|
180537
|
+
"Report DocBridge version drift and reconcile the managed agent skill.",
|
|
180538
|
+
"Use it after a DocBridge release, or when a project still carries assets",
|
|
180539
|
+
"from an older layout. `--check` is read-only. Applying changes touches",
|
|
180540
|
+
"only the managed docbridge skill directory and the known legacy skill",
|
|
180541
|
+
"directories; it never edits config, hooks, CI recipes, or user code, and",
|
|
180542
|
+
"it never runs a package manager for you."
|
|
180543
|
+
].join(`
|
|
180544
|
+
`),
|
|
180545
|
+
options: [
|
|
180546
|
+
{
|
|
180547
|
+
flag: "--root <path>",
|
|
180548
|
+
description: "Project root to inspect. Defaults to current directory."
|
|
180549
|
+
},
|
|
180550
|
+
{
|
|
180551
|
+
flag: "--check",
|
|
180552
|
+
description: "Report version and managed asset state without writing files."
|
|
180553
|
+
},
|
|
180554
|
+
{
|
|
180555
|
+
flag: "--dry-run",
|
|
180556
|
+
description: "Print the operations that would be applied without writing files."
|
|
180557
|
+
},
|
|
180558
|
+
{ flag: "--yes", description: "Confirm destructive operations without prompting." },
|
|
180559
|
+
{
|
|
180560
|
+
flag: "--force",
|
|
180561
|
+
description: "Replace the managed skill and remove known legacy skill directories."
|
|
180562
|
+
},
|
|
180563
|
+
{
|
|
180564
|
+
flag: "--agent-target <target>",
|
|
180565
|
+
description: "Agent target: codex, claude, both, or none."
|
|
180566
|
+
}
|
|
180567
|
+
]
|
|
180568
|
+
},
|
|
179904
180569
|
init: {
|
|
179905
180570
|
usage: "[options]",
|
|
179906
180571
|
summary: "Use once per project to set up config and the agent skill.",
|
|
@@ -179968,7 +180633,7 @@ function commandList() {
|
|
|
179968
180633
|
return SUBCOMMANDS.map((command) => ` ${command.padEnd(width)}${COMMAND_DOCS[command].summary}`).join(`
|
|
179969
180634
|
`);
|
|
179970
180635
|
}
|
|
179971
|
-
var GLOBAL_OPTION_SECTIONS = ["check", "related", "context", "graph", "docs", "init"].map((command) => {
|
|
180636
|
+
var GLOBAL_OPTION_SECTIONS = ["check", "related", "context", "graph", "docs", "init", "upgrade"].map((command) => {
|
|
179972
180637
|
const label = `${command.charAt(0).toUpperCase()}${command.slice(1)}`;
|
|
179973
180638
|
return `${label} options:
|
|
179974
180639
|
${renderOptions(COMMAND_DOCS[command].options)}`;
|
|
@@ -180065,20 +180730,12 @@ function hasHelpFlag(args) {
|
|
|
180065
180730
|
}
|
|
180066
180731
|
|
|
180067
180732
|
// src/cli/init.ts
|
|
180068
|
-
import {
|
|
180069
|
-
|
|
180070
|
-
lstatSync as lstatSync3,
|
|
180071
|
-
mkdirSync as mkdirSync2,
|
|
180072
|
-
readFileSync as readFileSync5,
|
|
180073
|
-
rmSync,
|
|
180074
|
-
statSync as statSync3,
|
|
180075
|
-
writeFileSync
|
|
180076
|
-
} from "node:fs";
|
|
180077
|
-
import { join as join8, resolve as resolve3 } from "node:path";
|
|
180733
|
+
import { readFileSync as readFileSync8, statSync as statSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
180734
|
+
import { join as join12, resolve as resolve3 } from "node:path";
|
|
180078
180735
|
|
|
180079
|
-
// src/
|
|
180080
|
-
import { existsSync as existsSync3, readdirSync as
|
|
180081
|
-
import { basename, dirname as
|
|
180736
|
+
// src/setup/init-discovery.ts
|
|
180737
|
+
import { existsSync as existsSync3, readdirSync as readdirSync3, statSync as statSync2 } from "node:fs";
|
|
180738
|
+
import { basename, dirname as dirname5, join as join9 } from "node:path";
|
|
180082
180739
|
var HIGH_CONFIDENCE_DIR_NAMES = new Set([
|
|
180083
180740
|
"docs/specs",
|
|
180084
180741
|
"specs",
|
|
@@ -180143,7 +180800,7 @@ function discoverDocsScope(projectRoot) {
|
|
|
180143
180800
|
}
|
|
180144
180801
|
const grouped = new Map;
|
|
180145
180802
|
for (const filePath of eligible) {
|
|
180146
|
-
const directory =
|
|
180803
|
+
const directory = dirname5(filePath);
|
|
180147
180804
|
const existing = grouped.get(directory) ?? [];
|
|
180148
180805
|
existing.push(filePath);
|
|
180149
180806
|
grouped.set(directory, existing);
|
|
@@ -180205,8 +180862,8 @@ function discoverCodeScope(projectRoot) {
|
|
|
180205
180862
|
};
|
|
180206
180863
|
}
|
|
180207
180864
|
function discoverAgentTarget(projectRoot) {
|
|
180208
|
-
const hasAgentsDir = existsSync3(
|
|
180209
|
-
const hasClaudeDir = existsSync3(
|
|
180865
|
+
const hasAgentsDir = existsSync3(join9(projectRoot, ".agents"));
|
|
180866
|
+
const hasClaudeDir = existsSync3(join9(projectRoot, ".claude"));
|
|
180210
180867
|
let defaultTarget = "none";
|
|
180211
180868
|
let recommendedTarget = "none";
|
|
180212
180869
|
let message;
|
|
@@ -180233,10 +180890,10 @@ function discoverAgentTarget(projectRoot) {
|
|
|
180233
180890
|
};
|
|
180234
180891
|
}
|
|
180235
180892
|
function collectMarkdownFiles(projectRoot, currentDir = ".") {
|
|
180236
|
-
const absoluteDir =
|
|
180893
|
+
const absoluteDir = join9(projectRoot, currentDir);
|
|
180237
180894
|
let entries;
|
|
180238
180895
|
try {
|
|
180239
|
-
entries =
|
|
180896
|
+
entries = readdirSync3(absoluteDir);
|
|
180240
180897
|
} catch {
|
|
180241
180898
|
return [];
|
|
180242
180899
|
}
|
|
@@ -180247,7 +180904,7 @@ function collectMarkdownFiles(projectRoot, currentDir = ".") {
|
|
|
180247
180904
|
if (segments.some((segment) => IGNORED_WALK_SEGMENTS.has(segment) || segment.startsWith("."))) {
|
|
180248
180905
|
continue;
|
|
180249
180906
|
}
|
|
180250
|
-
const absolutePath =
|
|
180907
|
+
const absolutePath = join9(projectRoot, relPath);
|
|
180251
180908
|
let stats;
|
|
180252
180909
|
try {
|
|
180253
180910
|
stats = statSync2(absolutePath);
|
|
@@ -180334,6 +180991,491 @@ function isExcludedCodeFile(filePath, language) {
|
|
|
180334
180991
|
return lower.includes(".generated.");
|
|
180335
180992
|
}
|
|
180336
180993
|
|
|
180994
|
+
// src/setup/init-plan.ts
|
|
180995
|
+
import { existsSync as existsSync4, readdirSync as readdirSync5, readFileSync as readFileSync7 } from "node:fs";
|
|
180996
|
+
import { join as join11, relative as relative3 } from "node:path";
|
|
180997
|
+
|
|
180998
|
+
// src/setup/skill-assets.ts
|
|
180999
|
+
import { cpSync, lstatSync as lstatSync2, mkdirSync as mkdirSync3, readdirSync as readdirSync4, readFileSync as readFileSync6, rmSync } from "node:fs";
|
|
181000
|
+
import { join as join10 } from "node:path";
|
|
181001
|
+
function classifyManagedPath(projectRoot, relativePath) {
|
|
181002
|
+
const segments = relativePath.split(/[/\\]/).filter((segment) => segment.length > 0);
|
|
181003
|
+
let current = projectRoot;
|
|
181004
|
+
for (const [index, segment] of segments.entries()) {
|
|
181005
|
+
current = join10(current, segment);
|
|
181006
|
+
const isLast = index === segments.length - 1;
|
|
181007
|
+
let stats;
|
|
181008
|
+
try {
|
|
181009
|
+
stats = lstatSync2(current);
|
|
181010
|
+
} catch {
|
|
181011
|
+
return "absent";
|
|
181012
|
+
}
|
|
181013
|
+
if (stats.isSymbolicLink()) {
|
|
181014
|
+
return isLast ? "symlink" : "symlinked-parent";
|
|
181015
|
+
}
|
|
181016
|
+
if (!stats.isDirectory()) {
|
|
181017
|
+
return isLast ? "non-directory" : "blocked-parent";
|
|
181018
|
+
}
|
|
181019
|
+
}
|
|
181020
|
+
return segments.length === 0 ? "blocked-parent" : "directory";
|
|
181021
|
+
}
|
|
181022
|
+
function unmanageablePathMessage(path, kind) {
|
|
181023
|
+
switch (kind) {
|
|
181024
|
+
case "symlink":
|
|
181025
|
+
return `Skill directory ${path} is a symlink and was left in place.`;
|
|
181026
|
+
case "symlinked-parent":
|
|
181027
|
+
return `Skill directory ${path} sits under a symlinked directory and was left in place, because writing through it would leave the project root.`;
|
|
181028
|
+
case "non-directory":
|
|
181029
|
+
return `${path} exists but is not a directory, so it was left in place.`;
|
|
181030
|
+
case "blocked-parent":
|
|
181031
|
+
return `Skill directory ${path} cannot be reached because a parent path component is not a directory, so it was left in place.`;
|
|
181032
|
+
default:
|
|
181033
|
+
return `Skill directory ${path} was left in place.`;
|
|
181034
|
+
}
|
|
181035
|
+
}
|
|
181036
|
+
function listSkillFiles(directory) {
|
|
181037
|
+
const files = [];
|
|
181038
|
+
collectSkillFiles(directory, "", files);
|
|
181039
|
+
return files.toSorted();
|
|
181040
|
+
}
|
|
181041
|
+
function compareSkillTree(installedDir, templateDir) {
|
|
181042
|
+
const installedFiles = new Set(listSkillFiles(installedDir));
|
|
181043
|
+
const templateFiles = listSkillFiles(templateDir);
|
|
181044
|
+
const modified = [];
|
|
181045
|
+
const missing = [];
|
|
181046
|
+
for (const file of templateFiles) {
|
|
181047
|
+
if (!installedFiles.has(file)) {
|
|
181048
|
+
missing.push(file);
|
|
181049
|
+
continue;
|
|
181050
|
+
}
|
|
181051
|
+
installedFiles.delete(file);
|
|
181052
|
+
if (readFileOrUndefined(join10(installedDir, file)) !== readFileOrUndefined(join10(templateDir, file))) {
|
|
181053
|
+
modified.push(file);
|
|
181054
|
+
}
|
|
181055
|
+
}
|
|
181056
|
+
return { modified, missing, extra: [...installedFiles].toSorted() };
|
|
181057
|
+
}
|
|
181058
|
+
function applySkillOperation(projectRoot, packageRoot, operation) {
|
|
181059
|
+
if (operation.action !== "create" && operation.action !== "overwrite" && operation.action !== "remove") {
|
|
181060
|
+
return;
|
|
181061
|
+
}
|
|
181062
|
+
const kind = classifyManagedPath(projectRoot, operation.path);
|
|
181063
|
+
const destinationDir = join10(projectRoot, operation.path);
|
|
181064
|
+
if (operation.action === "remove") {
|
|
181065
|
+
if (kind === "directory") {
|
|
181066
|
+
rmSync(destinationDir, { recursive: true, force: true });
|
|
181067
|
+
}
|
|
181068
|
+
return;
|
|
181069
|
+
}
|
|
181070
|
+
if (kind !== "absent" && kind !== "directory") {
|
|
181071
|
+
return;
|
|
181072
|
+
}
|
|
181073
|
+
const skillName = operation.path.split(/[/\\]/).at(-1);
|
|
181074
|
+
if (skillName === undefined || skillName.length === 0) {
|
|
181075
|
+
return;
|
|
181076
|
+
}
|
|
181077
|
+
if (operation.action === "overwrite" && kind === "directory") {
|
|
181078
|
+
rmSync(destinationDir, { recursive: true, force: true });
|
|
181079
|
+
}
|
|
181080
|
+
mkdirSync3(destinationDir, { recursive: true });
|
|
181081
|
+
cpSync(join10(packageRoot, "templates", "skills", skillName), destinationDir, {
|
|
181082
|
+
recursive: true,
|
|
181083
|
+
force: true
|
|
181084
|
+
});
|
|
181085
|
+
}
|
|
181086
|
+
function collectSkillFiles(root, prefix, files) {
|
|
181087
|
+
let entries;
|
|
181088
|
+
try {
|
|
181089
|
+
entries = readdirSync4(join10(root, prefix), { withFileTypes: true });
|
|
181090
|
+
} catch {
|
|
181091
|
+
return;
|
|
181092
|
+
}
|
|
181093
|
+
for (const entry of entries.toSorted((left, right) => left.name < right.name ? -1 : 1)) {
|
|
181094
|
+
const relativePath = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
181095
|
+
if (entry.isDirectory()) {
|
|
181096
|
+
collectSkillFiles(root, relativePath, files);
|
|
181097
|
+
continue;
|
|
181098
|
+
}
|
|
181099
|
+
files.push(relativePath);
|
|
181100
|
+
}
|
|
181101
|
+
}
|
|
181102
|
+
function readFileOrUndefined(path) {
|
|
181103
|
+
try {
|
|
181104
|
+
return readFileSync6(path, "utf8");
|
|
181105
|
+
} catch {
|
|
181106
|
+
return;
|
|
181107
|
+
}
|
|
181108
|
+
}
|
|
181109
|
+
|
|
181110
|
+
// src/setup/init-plan.ts
|
|
181111
|
+
var CONFIG_FILE_NAME2 = "docbridge.config.json";
|
|
181112
|
+
var INIT_SKILL_NAMES = ["docbridge"];
|
|
181113
|
+
var LEGACY_SKILL_NAMES = [
|
|
181114
|
+
"docbridge-adopt",
|
|
181115
|
+
"docbridge-annotate",
|
|
181116
|
+
"docbridge-link",
|
|
181117
|
+
"docbridge-review",
|
|
181118
|
+
"docbridge-sync"
|
|
181119
|
+
];
|
|
181120
|
+
function planInitCommand(input) {
|
|
181121
|
+
const packageRoot = input.packageRoot ?? resolvePackageRoot();
|
|
181122
|
+
const existingConfig = readExistingConfig(input.projectRoot);
|
|
181123
|
+
const messages = [];
|
|
181124
|
+
const nextSteps = [];
|
|
181125
|
+
let exitCode = 0;
|
|
181126
|
+
const agentResolution = input.command === "init" ? resolveInitAgentTarget(input.discovery, input.options) : resolveInitWithAgentTarget(input.discovery, input.options);
|
|
181127
|
+
if (agentResolution.error !== undefined) {
|
|
181128
|
+
return {
|
|
181129
|
+
command: input.command,
|
|
181130
|
+
configOps: [],
|
|
181131
|
+
skillOps: [],
|
|
181132
|
+
messages: [agentResolution.error],
|
|
181133
|
+
nextSteps: [],
|
|
181134
|
+
agentGuidance: [],
|
|
181135
|
+
exitCode: 1
|
|
181136
|
+
};
|
|
181137
|
+
}
|
|
181138
|
+
const agentTarget = agentResolution.target ?? "none";
|
|
181139
|
+
const configOps = input.command === "init" ? planConfigOperations({
|
|
181140
|
+
projectRoot: input.projectRoot,
|
|
181141
|
+
options: input.options,
|
|
181142
|
+
discovery: input.discovery,
|
|
181143
|
+
confirmedScope: input.confirmedScope,
|
|
181144
|
+
existingConfig,
|
|
181145
|
+
messages
|
|
181146
|
+
}) : [];
|
|
181147
|
+
if (configOps.some((operation) => operation.action === "create" || operation.action === "would-create")) {
|
|
181148
|
+
nextSteps.push("Review docbridge.config.json and adjust include.docs or include.code if needed.");
|
|
181149
|
+
nextSteps.push("Add @doc and @code annotations to link code and documentation.");
|
|
181150
|
+
nextSteps.push("Run docbridge check when links exist.");
|
|
181151
|
+
} else if (existingConfig.ok) {
|
|
181152
|
+
messages.push("Existing docbridge.config.json was left unchanged.");
|
|
181153
|
+
summarizeExistingConfig(existingConfig.config, messages);
|
|
181154
|
+
} else if (existingConfig.rawText !== undefined) {
|
|
181155
|
+
messages.push("Existing docbridge.config.json is invalid and was not modified.");
|
|
181156
|
+
for (const diagnostic of existingConfig.diagnostics) {
|
|
181157
|
+
messages.push(diagnostic.message);
|
|
181158
|
+
}
|
|
181159
|
+
messages.push("Repair the config manually or delete it before re-running init.");
|
|
181160
|
+
exitCode = 1;
|
|
181161
|
+
}
|
|
181162
|
+
const skillOps = planSkillOperations({
|
|
181163
|
+
projectRoot: input.projectRoot,
|
|
181164
|
+
packageRoot,
|
|
181165
|
+
agentTarget,
|
|
181166
|
+
options: input.options,
|
|
181167
|
+
messages
|
|
181168
|
+
});
|
|
181169
|
+
const agentGuidance = input.command === "init-with-agent" && agentTarget !== "none" ? buildAgentGuidance(input.projectRoot, agentTarget) : [];
|
|
181170
|
+
if (input.command === "init-with-agent") {
|
|
181171
|
+
nextSteps.push("Run the printed one-shot command or fallback prompt in your agent.");
|
|
181172
|
+
nextSteps.push("Let the docbridge skill confirm scope and create or improve docbridge.config.json.");
|
|
181173
|
+
}
|
|
181174
|
+
if (input.options.yes && input.command === "init" && configOps.length === 0 && existingConfig.rawText === undefined && (input.discovery.docs.ambiguous || input.discovery.code.ambiguous)) {
|
|
181175
|
+
exitCode = 1;
|
|
181176
|
+
}
|
|
181177
|
+
return {
|
|
181178
|
+
command: input.command,
|
|
181179
|
+
configOps,
|
|
181180
|
+
skillOps,
|
|
181181
|
+
messages,
|
|
181182
|
+
nextSteps,
|
|
181183
|
+
agentGuidance,
|
|
181184
|
+
exitCode
|
|
181185
|
+
};
|
|
181186
|
+
}
|
|
181187
|
+
function buildConfigFromScope(scope) {
|
|
181188
|
+
const code = {};
|
|
181189
|
+
for (const language of scope.languages) {
|
|
181190
|
+
code[language.language] = { patterns: [...language.patterns] };
|
|
181191
|
+
}
|
|
181192
|
+
return {
|
|
181193
|
+
include: {
|
|
181194
|
+
code,
|
|
181195
|
+
docs: [scope.docsPattern]
|
|
181196
|
+
}
|
|
181197
|
+
};
|
|
181198
|
+
}
|
|
181199
|
+
function listDistributableSkills(packageRoot) {
|
|
181200
|
+
const skillsRoot = join11(packageRoot, "templates", "skills");
|
|
181201
|
+
let entries;
|
|
181202
|
+
try {
|
|
181203
|
+
entries = readdirSync5(skillsRoot);
|
|
181204
|
+
} catch {
|
|
181205
|
+
return [];
|
|
181206
|
+
}
|
|
181207
|
+
return entries.filter((entry) => entry.startsWith("docbridge")).filter((entry) => existsSync4(join11(skillsRoot, entry, "SKILL.md"))).toSorted();
|
|
181208
|
+
}
|
|
181209
|
+
function formatInitPlan(plan) {
|
|
181210
|
+
const lines = [];
|
|
181211
|
+
for (const message of plan.messages) {
|
|
181212
|
+
lines.push(message);
|
|
181213
|
+
}
|
|
181214
|
+
if (plan.configOps.length > 0) {
|
|
181215
|
+
lines.push("");
|
|
181216
|
+
lines.push("Config:");
|
|
181217
|
+
for (const operation of plan.configOps) {
|
|
181218
|
+
lines.push(formatFileOp(operation));
|
|
181219
|
+
if (operation.content !== undefined) {
|
|
181220
|
+
lines.push(operation.content);
|
|
181221
|
+
}
|
|
181222
|
+
}
|
|
181223
|
+
}
|
|
181224
|
+
if (plan.skillOps.length > 0) {
|
|
181225
|
+
lines.push("");
|
|
181226
|
+
lines.push("Skills:");
|
|
181227
|
+
for (const operation of plan.skillOps) {
|
|
181228
|
+
lines.push(formatFileOp(operation));
|
|
181229
|
+
}
|
|
181230
|
+
}
|
|
181231
|
+
if (plan.agentGuidance.length > 0) {
|
|
181232
|
+
lines.push("");
|
|
181233
|
+
lines.push("Agent setup:");
|
|
181234
|
+
for (const guidance of plan.agentGuidance) {
|
|
181235
|
+
lines.push("");
|
|
181236
|
+
lines.push(`${capitalize(guidance.agent)} destination: ${guidance.destination}`);
|
|
181237
|
+
lines.push(`One-shot command: ${guidance.oneShotCommand}`);
|
|
181238
|
+
lines.push("Fallback prompt:");
|
|
181239
|
+
lines.push(guidance.fallbackPrompt);
|
|
181240
|
+
}
|
|
181241
|
+
}
|
|
181242
|
+
if (plan.nextSteps.length > 0) {
|
|
181243
|
+
lines.push("");
|
|
181244
|
+
lines.push("Next steps:");
|
|
181245
|
+
for (const step of plan.nextSteps) {
|
|
181246
|
+
lines.push(`- ${step}`);
|
|
181247
|
+
}
|
|
181248
|
+
}
|
|
181249
|
+
return `${lines.join(`
|
|
181250
|
+
`)}
|
|
181251
|
+
`;
|
|
181252
|
+
}
|
|
181253
|
+
function planConfigOperations(input) {
|
|
181254
|
+
if (input.existingConfig.rawText !== undefined) {
|
|
181255
|
+
return [];
|
|
181256
|
+
}
|
|
181257
|
+
const scope = input.confirmedScope ?? inferConfirmedScope(input.discovery, input.options);
|
|
181258
|
+
if (scope === undefined) {
|
|
181259
|
+
if (input.discovery.docs.message !== undefined) {
|
|
181260
|
+
input.messages.push(input.discovery.docs.message);
|
|
181261
|
+
}
|
|
181262
|
+
if (input.discovery.code.message !== undefined) {
|
|
181263
|
+
input.messages.push(input.discovery.code.message);
|
|
181264
|
+
}
|
|
181265
|
+
if (input.options.yes) {
|
|
181266
|
+
input.messages.push("Automatic setup stopped because docs or code scope is ambiguous. Re-run interactively or confirm scope explicitly.");
|
|
181267
|
+
}
|
|
181268
|
+
return [];
|
|
181269
|
+
}
|
|
181270
|
+
const config = buildConfigFromScope(scope);
|
|
181271
|
+
const content = `${JSON.stringify({ $schema: "./schemas/docbridge.schema.json", ...config }, null, 2)}
|
|
181272
|
+
`;
|
|
181273
|
+
const action = input.options.dryRun ? "would-create" : "create";
|
|
181274
|
+
input.messages.push(`Docs scope: ${scope.docsPattern}`);
|
|
181275
|
+
input.messages.push(`Code scope: ${scope.languages.map((language) => `${language.language} (${language.patterns.join(", ")})`).join("; ")}`);
|
|
181276
|
+
return [
|
|
181277
|
+
{
|
|
181278
|
+
action,
|
|
181279
|
+
path: CONFIG_FILE_NAME2,
|
|
181280
|
+
content,
|
|
181281
|
+
reason: "Create docbridge.config.json from discovered scope."
|
|
181282
|
+
}
|
|
181283
|
+
];
|
|
181284
|
+
}
|
|
181285
|
+
function inferConfirmedScope(discovery, options) {
|
|
181286
|
+
if (discovery.docs.ambiguous || discovery.code.ambiguous) {
|
|
181287
|
+
return;
|
|
181288
|
+
}
|
|
181289
|
+
if (discovery.docs.recommended === undefined || discovery.code.languages.length === 0) {
|
|
181290
|
+
return;
|
|
181291
|
+
}
|
|
181292
|
+
if (!options.yes) {
|
|
181293
|
+
return;
|
|
181294
|
+
}
|
|
181295
|
+
return {
|
|
181296
|
+
docsPattern: discovery.docs.recommended.pattern,
|
|
181297
|
+
languages: discovery.code.languages
|
|
181298
|
+
};
|
|
181299
|
+
}
|
|
181300
|
+
function planSkillOperations(input) {
|
|
181301
|
+
if (input.agentTarget === "none") {
|
|
181302
|
+
return [];
|
|
181303
|
+
}
|
|
181304
|
+
const destinations = agentDestinations(input.agentTarget);
|
|
181305
|
+
const availableSkills = new Set(listDistributableSkills(input.packageRoot));
|
|
181306
|
+
const operations = [];
|
|
181307
|
+
for (const destination of destinations) {
|
|
181308
|
+
for (const skillName of INIT_SKILL_NAMES) {
|
|
181309
|
+
if (!availableSkills.has(skillName)) {
|
|
181310
|
+
continue;
|
|
181311
|
+
}
|
|
181312
|
+
const destinationDir = join11(input.projectRoot, destination, skillName);
|
|
181313
|
+
const path = relative3(input.projectRoot, destinationDir);
|
|
181314
|
+
const kind = classifyManagedPath(input.projectRoot, path);
|
|
181315
|
+
if (kind !== "absent" && kind !== "directory") {
|
|
181316
|
+
input.messages.push(unmanageablePathMessage(path, kind));
|
|
181317
|
+
continue;
|
|
181318
|
+
}
|
|
181319
|
+
const exists = kind === "directory";
|
|
181320
|
+
let action;
|
|
181321
|
+
let reason;
|
|
181322
|
+
if (exists && input.options.force) {
|
|
181323
|
+
action = input.options.dryRun ? "would-overwrite" : "overwrite";
|
|
181324
|
+
reason = "Overwrite existing skill directory.";
|
|
181325
|
+
} else if (exists) {
|
|
181326
|
+
action = "skip";
|
|
181327
|
+
reason = "Skill directory already exists.";
|
|
181328
|
+
} else {
|
|
181329
|
+
action = input.options.dryRun ? "would-create" : "create";
|
|
181330
|
+
reason = "Install distributable skill template.";
|
|
181331
|
+
}
|
|
181332
|
+
operations.push({
|
|
181333
|
+
action,
|
|
181334
|
+
path,
|
|
181335
|
+
reason
|
|
181336
|
+
});
|
|
181337
|
+
}
|
|
181338
|
+
operations.push(...planLegacySkillOperations({
|
|
181339
|
+
projectRoot: input.projectRoot,
|
|
181340
|
+
destination,
|
|
181341
|
+
options: input.options,
|
|
181342
|
+
messages: input.messages
|
|
181343
|
+
}));
|
|
181344
|
+
}
|
|
181345
|
+
return operations;
|
|
181346
|
+
}
|
|
181347
|
+
function planLegacySkillOperations(input) {
|
|
181348
|
+
const operations = [];
|
|
181349
|
+
const leftover = [];
|
|
181350
|
+
for (const skillName of LEGACY_SKILL_NAMES) {
|
|
181351
|
+
const destinationDir = join11(input.projectRoot, input.destination, skillName);
|
|
181352
|
+
const path = relative3(input.projectRoot, destinationDir);
|
|
181353
|
+
const kind = classifyManagedPath(input.projectRoot, path);
|
|
181354
|
+
if (kind === "absent") {
|
|
181355
|
+
continue;
|
|
181356
|
+
}
|
|
181357
|
+
if (kind !== "directory") {
|
|
181358
|
+
input.messages.push(unmanageablePathMessage(path, kind));
|
|
181359
|
+
continue;
|
|
181360
|
+
}
|
|
181361
|
+
if (input.options.force) {
|
|
181362
|
+
operations.push({
|
|
181363
|
+
action: input.options.dryRun ? "would-remove" : "remove",
|
|
181364
|
+
path,
|
|
181365
|
+
reason: "Remove leftover skill from the previous five-skill layout."
|
|
181366
|
+
});
|
|
181367
|
+
} else {
|
|
181368
|
+
leftover.push(path);
|
|
181369
|
+
}
|
|
181370
|
+
}
|
|
181371
|
+
if (leftover.length > 0) {
|
|
181372
|
+
input.messages.push(`Legacy DocBridge skill directories were left in place (${leftover.join(", ")}). Re-run with --force to remove them after reviewing local edits.`);
|
|
181373
|
+
}
|
|
181374
|
+
return operations;
|
|
181375
|
+
}
|
|
181376
|
+
function buildAgentGuidance(projectRoot, agentTarget) {
|
|
181377
|
+
const guidance = [];
|
|
181378
|
+
if (agentTarget === "codex" || agentTarget === "both") {
|
|
181379
|
+
guidance.push({
|
|
181380
|
+
agent: "codex",
|
|
181381
|
+
destination: ".agents/skills/docbridge/",
|
|
181382
|
+
oneShotCommand: `cd ${projectRoot} && codex --prompt "Use the docbridge skill to adopt DocBridge in this repository."`,
|
|
181383
|
+
fallbackPrompt: buildFallbackPrompt(projectRoot, "codex")
|
|
181384
|
+
});
|
|
181385
|
+
}
|
|
181386
|
+
if (agentTarget === "claude" || agentTarget === "both") {
|
|
181387
|
+
guidance.push({
|
|
181388
|
+
agent: "claude",
|
|
181389
|
+
destination: ".claude/skills/docbridge/",
|
|
181390
|
+
oneShotCommand: `cd ${projectRoot} && /docbridge adopt DocBridge in this repository`,
|
|
181391
|
+
fallbackPrompt: buildFallbackPrompt(projectRoot, "claude")
|
|
181392
|
+
});
|
|
181393
|
+
}
|
|
181394
|
+
return guidance;
|
|
181395
|
+
}
|
|
181396
|
+
function buildFallbackPrompt(projectRoot, agent) {
|
|
181397
|
+
const invocation = agent === "claude" ? "Use the docbridge skill" : "Use the docbridge skill from .agents/skills/docbridge";
|
|
181398
|
+
return `${invocation} to adopt DocBridge in ${projectRoot}. Confirm docs and code scope, create or improve docbridge.config.json, and suggest the next linking steps.`;
|
|
181399
|
+
}
|
|
181400
|
+
function resolveInitAgentTarget(discovery, options) {
|
|
181401
|
+
if (options.agentTarget !== undefined) {
|
|
181402
|
+
return { target: options.agentTarget, error: undefined };
|
|
181403
|
+
}
|
|
181404
|
+
if (options.yes) {
|
|
181405
|
+
return { target: discovery.agent.defaultTarget, error: undefined };
|
|
181406
|
+
}
|
|
181407
|
+
return { target: discovery.agent.recommendedTarget, error: undefined };
|
|
181408
|
+
}
|
|
181409
|
+
function resolveInitWithAgentTarget(discovery, options) {
|
|
181410
|
+
if (options.agentTarget !== undefined) {
|
|
181411
|
+
if (options.agentTarget === "none") {
|
|
181412
|
+
return {
|
|
181413
|
+
target: undefined,
|
|
181414
|
+
error: "init-with-agent requires an agent target other than none."
|
|
181415
|
+
};
|
|
181416
|
+
}
|
|
181417
|
+
return { target: options.agentTarget, error: undefined };
|
|
181418
|
+
}
|
|
181419
|
+
if (discovery.agent.defaultTarget === "none") {
|
|
181420
|
+
if (options.yes) {
|
|
181421
|
+
return {
|
|
181422
|
+
target: undefined,
|
|
181423
|
+
error: "init-with-agent --yes requires an explicit agent target when no .agents/ or .claude/ directory exists. Pass --agent-target codex, claude, or both."
|
|
181424
|
+
};
|
|
181425
|
+
}
|
|
181426
|
+
return { target: discovery.agent.recommendedTarget, error: undefined };
|
|
181427
|
+
}
|
|
181428
|
+
return { target: discovery.agent.defaultTarget, error: undefined };
|
|
181429
|
+
}
|
|
181430
|
+
function readExistingConfig(projectRoot) {
|
|
181431
|
+
const configPath = join11(projectRoot, CONFIG_FILE_NAME2);
|
|
181432
|
+
let rawText;
|
|
181433
|
+
try {
|
|
181434
|
+
rawText = readFileSync7(configPath, "utf8");
|
|
181435
|
+
} catch {
|
|
181436
|
+
rawText = undefined;
|
|
181437
|
+
}
|
|
181438
|
+
const resolved = resolveConfig(rawText);
|
|
181439
|
+
return {
|
|
181440
|
+
rawText,
|
|
181441
|
+
ok: resolved.ok,
|
|
181442
|
+
config: resolved.config,
|
|
181443
|
+
diagnostics: resolved.diagnostics
|
|
181444
|
+
};
|
|
181445
|
+
}
|
|
181446
|
+
function summarizeExistingConfig(config, messages) {
|
|
181447
|
+
messages.push(`Docs scope: ${config.include.docs.join(", ")}`);
|
|
181448
|
+
const codeSummary = Object.entries(config.include.code).map(([language, entry]) => `${language}: ${entry?.patterns.join(", ") ?? ""}`).join("; ");
|
|
181449
|
+
messages.push(`Code scope: ${codeSummary}`);
|
|
181450
|
+
}
|
|
181451
|
+
function agentDestinations(agentTarget) {
|
|
181452
|
+
switch (agentTarget) {
|
|
181453
|
+
case "codex":
|
|
181454
|
+
return [".agents/skills"];
|
|
181455
|
+
case "claude":
|
|
181456
|
+
return [".claude/skills"];
|
|
181457
|
+
case "both":
|
|
181458
|
+
return [".agents/skills", ".claude/skills"];
|
|
181459
|
+
default:
|
|
181460
|
+
return [];
|
|
181461
|
+
}
|
|
181462
|
+
}
|
|
181463
|
+
function formatFileOp(operation) {
|
|
181464
|
+
let label = operation.action;
|
|
181465
|
+
if (operation.action === "would-create") {
|
|
181466
|
+
label = "would create";
|
|
181467
|
+
} else if (operation.action === "would-overwrite") {
|
|
181468
|
+
label = "would overwrite";
|
|
181469
|
+
} else if (operation.action === "would-remove") {
|
|
181470
|
+
label = "would remove";
|
|
181471
|
+
}
|
|
181472
|
+
const reason = operation.reason ? ` (${operation.reason})` : "";
|
|
181473
|
+
return `- ${label} ${operation.path}${reason}`;
|
|
181474
|
+
}
|
|
181475
|
+
function capitalize(value) {
|
|
181476
|
+
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
181477
|
+
}
|
|
181478
|
+
|
|
180337
181479
|
// src/cli/init.ts
|
|
180338
181480
|
var AGENT_TARGETS = new Set(["codex", "claude", "both", "none"]);
|
|
180339
181481
|
function parseInitOptions(args, command) {
|
|
@@ -180400,7 +181542,7 @@ function createDefaultPrompts(isInteractive = process.stdin.isTTY) {
|
|
|
180400
181542
|
return defaultValue;
|
|
180401
181543
|
}
|
|
180402
181544
|
process.stdout.write(`${message} ${defaultValue ? "[Y/n]" : "[y/N]"} `);
|
|
180403
|
-
const answer =
|
|
181545
|
+
const answer = readFileSync8(0, "utf8").trim().toLowerCase();
|
|
180404
181546
|
if (answer.length === 0) {
|
|
180405
181547
|
return defaultValue;
|
|
180406
181548
|
}
|
|
@@ -180417,7 +181559,7 @@ function createDefaultPrompts(isInteractive = process.stdin.isTTY) {
|
|
|
180417
181559
|
`);
|
|
180418
181560
|
}
|
|
180419
181561
|
process.stdout.write(`Choice [${defaultChoice}]: `);
|
|
180420
|
-
const answer =
|
|
181562
|
+
const answer = readFileSync8(0, "utf8").trim();
|
|
180421
181563
|
if (answer.length === 0) {
|
|
180422
181564
|
return defaultChoice;
|
|
180423
181565
|
}
|
|
@@ -180533,57 +181675,365 @@ function executePlan(projectRoot, plan, packageRoot) {
|
|
|
180533
181675
|
if (operation.action === "skip") {
|
|
180534
181676
|
continue;
|
|
180535
181677
|
}
|
|
180536
|
-
|
|
181678
|
+
applySkillOperation(projectRoot, packageRoot, operation);
|
|
180537
181679
|
}
|
|
180538
181680
|
}
|
|
180539
181681
|
function executeFileOperation(projectRoot, operation) {
|
|
180540
|
-
const absolutePath =
|
|
181682
|
+
const absolutePath = join12(projectRoot, operation.path);
|
|
180541
181683
|
if (operation.action === "create" && operation.content !== undefined) {
|
|
180542
|
-
|
|
181684
|
+
writeFileSync2(absolutePath, operation.content, "utf8");
|
|
180543
181685
|
}
|
|
180544
181686
|
if (operation.action === "overwrite" && operation.content !== undefined) {
|
|
180545
|
-
|
|
181687
|
+
writeFileSync2(absolutePath, operation.content, "utf8");
|
|
180546
181688
|
}
|
|
180547
181689
|
}
|
|
180548
|
-
function
|
|
180549
|
-
const
|
|
180550
|
-
|
|
180551
|
-
|
|
180552
|
-
|
|
180553
|
-
|
|
180554
|
-
|
|
180555
|
-
|
|
180556
|
-
|
|
181690
|
+
function resolveProjectRoot(root, command) {
|
|
181691
|
+
const projectRoot = resolve3(root);
|
|
181692
|
+
let stats;
|
|
181693
|
+
try {
|
|
181694
|
+
stats = statSync3(projectRoot);
|
|
181695
|
+
} catch {
|
|
181696
|
+
throw new InitCliError(`Root path does not exist: ${root}`, rootPathGuidance(command));
|
|
181697
|
+
}
|
|
181698
|
+
if (!stats.isDirectory()) {
|
|
181699
|
+
throw new InitCliError(`Root path is not a directory: ${root}`, rootPathGuidance(command));
|
|
181700
|
+
}
|
|
181701
|
+
return projectRoot;
|
|
181702
|
+
}
|
|
181703
|
+
|
|
181704
|
+
// src/cli/upgrade.ts
|
|
181705
|
+
import { statSync as statSync4 } from "node:fs";
|
|
181706
|
+
import { resolve as resolve4 } from "node:path";
|
|
181707
|
+
|
|
181708
|
+
// src/setup/upgrade-plan.ts
|
|
181709
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
181710
|
+
import { join as join13, relative as relative4 } from "node:path";
|
|
181711
|
+
var MANAGED_SKILL_NAME = INIT_SKILL_NAMES[0];
|
|
181712
|
+
function reportCliVersion(currentVersion, latest) {
|
|
181713
|
+
if (latest.status !== "ok") {
|
|
181714
|
+
return { current: currentVersion, latest: undefined, status: "unknown" };
|
|
181715
|
+
}
|
|
181716
|
+
if (isNewerStableVersion(latest.latest, currentVersion)) {
|
|
181717
|
+
return { current: currentVersion, latest: latest.latest, status: "outdated" };
|
|
181718
|
+
}
|
|
181719
|
+
if (isNewerStableVersion(currentVersion, latest.latest)) {
|
|
181720
|
+
return { current: currentVersion, latest: latest.latest, status: "ahead" };
|
|
181721
|
+
}
|
|
181722
|
+
return { current: currentVersion, latest: latest.latest, status: "up-to-date" };
|
|
181723
|
+
}
|
|
181724
|
+
function resolveUpgradeAgentTarget(discovery, explicitTarget) {
|
|
181725
|
+
return explicitTarget ?? discovery.agent.defaultTarget;
|
|
181726
|
+
}
|
|
181727
|
+
function inspectManagedSkill(input) {
|
|
181728
|
+
const destinationDir = join13(input.projectRoot, input.destination, MANAGED_SKILL_NAME);
|
|
181729
|
+
const path = toRootRelative(input.projectRoot, destinationDir);
|
|
181730
|
+
const empty = {
|
|
181731
|
+
destination: input.destination,
|
|
181732
|
+
path,
|
|
181733
|
+
modifiedFiles: [],
|
|
181734
|
+
missingFiles: [],
|
|
181735
|
+
extraFiles: []
|
|
181736
|
+
};
|
|
181737
|
+
const kind = classifyManagedPath(input.projectRoot, path);
|
|
181738
|
+
if (kind !== "directory") {
|
|
181739
|
+
return { ...empty, state: kind };
|
|
181740
|
+
}
|
|
181741
|
+
if (!input.templateAvailable) {
|
|
181742
|
+
return { ...empty, state: "template-missing" };
|
|
181743
|
+
}
|
|
181744
|
+
const comparison = compareSkillTree(destinationDir, join13(input.packageRoot, "templates", "skills", MANAGED_SKILL_NAME));
|
|
181745
|
+
const drifted = comparison.modified.length > 0 || comparison.missing.length > 0 || comparison.extra.length > 0;
|
|
181746
|
+
return {
|
|
181747
|
+
destination: input.destination,
|
|
181748
|
+
path,
|
|
181749
|
+
state: drifted ? "modified" : "up-to-date",
|
|
181750
|
+
modifiedFiles: comparison.modified,
|
|
181751
|
+
missingFiles: comparison.missing,
|
|
181752
|
+
extraFiles: comparison.extra
|
|
181753
|
+
};
|
|
181754
|
+
}
|
|
181755
|
+
function inspectLegacySkills(projectRoot, destination) {
|
|
181756
|
+
const reports = [];
|
|
181757
|
+
for (const skillName of LEGACY_SKILL_NAMES) {
|
|
181758
|
+
const destinationDir = join13(projectRoot, destination, skillName);
|
|
181759
|
+
const path = toRootRelative(projectRoot, destinationDir);
|
|
181760
|
+
const kind = classifyManagedPath(projectRoot, path);
|
|
181761
|
+
if (kind === "absent") {
|
|
181762
|
+
continue;
|
|
180557
181763
|
}
|
|
180558
|
-
|
|
181764
|
+
if (kind === "symlinked-parent" && !existsSync5(destinationDir)) {
|
|
181765
|
+
continue;
|
|
181766
|
+
}
|
|
181767
|
+
reports.push({ path, kind });
|
|
181768
|
+
}
|
|
181769
|
+
return reports;
|
|
181770
|
+
}
|
|
181771
|
+
function planUpgrade(input) {
|
|
181772
|
+
const mode = input.options.check ? "check" : "apply";
|
|
181773
|
+
const cli = reportCliVersion(input.currentVersion, input.latest);
|
|
181774
|
+
const agentTarget = resolveUpgradeAgentTarget(input.discovery, input.options.agentTarget);
|
|
181775
|
+
const templateAvailable = listDistributableSkills(input.packageRoot).includes(MANAGED_SKILL_NAME);
|
|
181776
|
+
const messages = [];
|
|
181777
|
+
const destinations = agentDestinations(agentTarget);
|
|
181778
|
+
const managedSkills = destinations.map((destination) => inspectManagedSkill({
|
|
181779
|
+
projectRoot: input.projectRoot,
|
|
181780
|
+
packageRoot: input.packageRoot,
|
|
181781
|
+
destination,
|
|
181782
|
+
templateAvailable
|
|
181783
|
+
}));
|
|
181784
|
+
const legacySkills = destinations.flatMap((destination) => inspectLegacySkills(input.projectRoot, destination));
|
|
181785
|
+
if (destinations.length === 0) {
|
|
181786
|
+
messages.push("No agent skill directory was selected, so no managed assets were inspected. Pass --agent-target codex, claude, or both to reconcile them.");
|
|
181787
|
+
}
|
|
181788
|
+
if (!templateAvailable && destinations.length > 0) {
|
|
181789
|
+
messages.push("The installed DocBridge package ships no docbridge skill template, so managed skills cannot be reconciled. Reinstall DocBridge.");
|
|
181790
|
+
}
|
|
181791
|
+
addVersionMessages(cli, input.guidance, messages);
|
|
181792
|
+
const pending = [];
|
|
181793
|
+
const operations = mode === "check" ? [] : planOperations({ managedSkills, legacySkills, options: input.options, pending });
|
|
181794
|
+
for (const report of managedSkills) {
|
|
181795
|
+
if (isUnmanageableState(report.state)) {
|
|
181796
|
+
messages.push(unmanageablePathMessage(report.path, report.state));
|
|
181797
|
+
}
|
|
181798
|
+
}
|
|
181799
|
+
for (const legacy of legacySkills) {
|
|
181800
|
+
if (legacy.kind !== "directory") {
|
|
181801
|
+
messages.push(unmanageablePathMessage(legacy.path, legacy.kind));
|
|
181802
|
+
}
|
|
181803
|
+
}
|
|
181804
|
+
return {
|
|
181805
|
+
mode,
|
|
181806
|
+
cli,
|
|
181807
|
+
guidance: input.guidance,
|
|
181808
|
+
managedSkills,
|
|
181809
|
+
legacySkills,
|
|
181810
|
+
operations,
|
|
181811
|
+
pending,
|
|
181812
|
+
messages,
|
|
181813
|
+
nextSteps: buildNextSteps({ mode, cli, options: input.options, pending }),
|
|
181814
|
+
requiresConfirmation: mode === "apply" && !input.options.dryRun && !input.options.yes && operations.some((operation) => operation.action === "overwrite" || operation.action === "remove"),
|
|
181815
|
+
exitCode: 0
|
|
181816
|
+
};
|
|
181817
|
+
}
|
|
181818
|
+
function planOperations(input) {
|
|
181819
|
+
const operations = [];
|
|
181820
|
+
for (const report of input.managedSkills) {
|
|
181821
|
+
if (report.state === "template-missing" || isUnmanageableState(report.state)) {
|
|
181822
|
+
continue;
|
|
181823
|
+
}
|
|
181824
|
+
if (report.state === "absent") {
|
|
181825
|
+
operations.push({
|
|
181826
|
+
action: input.options.dryRun ? "would-create" : "create",
|
|
181827
|
+
path: report.path,
|
|
181828
|
+
reason: "Install the managed docbridge skill."
|
|
181829
|
+
});
|
|
181830
|
+
continue;
|
|
181831
|
+
}
|
|
181832
|
+
if (!input.options.force) {
|
|
181833
|
+
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.`);
|
|
181834
|
+
continue;
|
|
181835
|
+
}
|
|
181836
|
+
operations.push({
|
|
181837
|
+
action: input.options.dryRun ? "would-overwrite" : "overwrite",
|
|
181838
|
+
path: report.path,
|
|
181839
|
+
reason: "Replace the managed docbridge skill with the packaged template."
|
|
181840
|
+
});
|
|
181841
|
+
}
|
|
181842
|
+
for (const legacy of input.legacySkills) {
|
|
181843
|
+
if (legacy.kind !== "directory") {
|
|
181844
|
+
continue;
|
|
181845
|
+
}
|
|
181846
|
+
if (!input.options.force) {
|
|
181847
|
+
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.`);
|
|
181848
|
+
continue;
|
|
181849
|
+
}
|
|
181850
|
+
operations.push({
|
|
181851
|
+
action: input.options.dryRun ? "would-remove" : "remove",
|
|
181852
|
+
path: legacy.path,
|
|
181853
|
+
reason: "Remove leftover skill from the previous five-skill layout."
|
|
181854
|
+
});
|
|
181855
|
+
}
|
|
181856
|
+
return operations;
|
|
181857
|
+
}
|
|
181858
|
+
function isUnmanageableState(state) {
|
|
181859
|
+
return state === "symlink" || state === "symlinked-parent" || state === "non-directory" || state === "blocked-parent";
|
|
181860
|
+
}
|
|
181861
|
+
function addVersionMessages(cli, guidance, messages) {
|
|
181862
|
+
if (cli.status === "outdated") {
|
|
181863
|
+
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.`);
|
|
180559
181864
|
return;
|
|
180560
181865
|
}
|
|
180561
|
-
if (
|
|
181866
|
+
if (cli.status === "unknown") {
|
|
181867
|
+
messages.push("The latest stable version could not be determined, so only local asset state is reported.");
|
|
181868
|
+
}
|
|
181869
|
+
}
|
|
181870
|
+
function buildNextSteps(input) {
|
|
181871
|
+
const nextSteps = [];
|
|
181872
|
+
if (input.cli.status === "outdated") {
|
|
181873
|
+
nextSteps.push("Upgrade the CLI with the command above, then re-run `docbridge upgrade`.");
|
|
181874
|
+
}
|
|
181875
|
+
if (input.mode === "check") {
|
|
181876
|
+
nextSteps.push("Run `docbridge upgrade --dry-run` to see the operations that would be applied.");
|
|
181877
|
+
}
|
|
181878
|
+
if (input.pending.length > 0) {
|
|
181879
|
+
nextSteps.push("Re-run with --force to apply the pending migrations listed above.");
|
|
181880
|
+
}
|
|
181881
|
+
if (input.mode === "apply" && input.options.dryRun) {
|
|
181882
|
+
nextSteps.push("Re-run without --dry-run to apply the plan.");
|
|
181883
|
+
}
|
|
181884
|
+
return nextSteps;
|
|
181885
|
+
}
|
|
181886
|
+
function formatUpgradePlan(plan) {
|
|
181887
|
+
const lines = [
|
|
181888
|
+
`DocBridge ${plan.cli.current} (latest stable: ${plan.cli.latest ?? "unknown"})`,
|
|
181889
|
+
`Status: ${plan.cli.status}`,
|
|
181890
|
+
...formatUpgradeGuidance(plan.guidance)
|
|
181891
|
+
];
|
|
181892
|
+
if (plan.messages.length > 0) {
|
|
181893
|
+
lines.push("", ...plan.messages);
|
|
181894
|
+
}
|
|
181895
|
+
appendSection(lines, "Managed skills:", plan.managedSkills.flatMap(formatManagedSkill));
|
|
181896
|
+
appendSection(lines, "Legacy skills:", plan.legacySkills.map((legacy) => `- ${legacy.path} (${legacy.kind})`));
|
|
181897
|
+
appendSection(lines, "Operations:", plan.operations.map((operation) => formatFileOp(operation)));
|
|
181898
|
+
appendSection(lines, "Pending migration:", plan.pending.map((entry) => `- ${entry}`));
|
|
181899
|
+
appendSection(lines, "Next steps:", plan.nextSteps.map((step) => `- ${step}`));
|
|
181900
|
+
return `${lines.join(`
|
|
181901
|
+
`)}
|
|
181902
|
+
`;
|
|
181903
|
+
}
|
|
181904
|
+
function formatManagedSkill(report) {
|
|
181905
|
+
const lines = [`- ${report.path}: ${report.state}`];
|
|
181906
|
+
appendFileList(lines, "changed", report.modifiedFiles);
|
|
181907
|
+
appendFileList(lines, "missing", report.missingFiles);
|
|
181908
|
+
appendFileList(lines, "extra", report.extraFiles);
|
|
181909
|
+
return lines;
|
|
181910
|
+
}
|
|
181911
|
+
function appendFileList(lines, label, files) {
|
|
181912
|
+
if (files.length > 0) {
|
|
181913
|
+
lines.push(` ${label}: ${files.join(", ")}`);
|
|
181914
|
+
}
|
|
181915
|
+
}
|
|
181916
|
+
function appendSection(lines, heading, entries) {
|
|
181917
|
+
if (entries.length === 0) {
|
|
180562
181918
|
return;
|
|
180563
181919
|
}
|
|
180564
|
-
|
|
180565
|
-
|
|
180566
|
-
|
|
181920
|
+
lines.push("", heading, ...entries);
|
|
181921
|
+
}
|
|
181922
|
+
function toRootRelative(projectRoot, absolutePath) {
|
|
181923
|
+
return relative4(projectRoot, absolutePath).split("\\").join("/");
|
|
181924
|
+
}
|
|
181925
|
+
|
|
181926
|
+
// src/cli/upgrade.ts
|
|
181927
|
+
var AGENT_TARGETS2 = new Set(["codex", "claude", "both", "none"]);
|
|
181928
|
+
function parseUpgradeOptions(args) {
|
|
181929
|
+
const options = {
|
|
181930
|
+
root: ".",
|
|
181931
|
+
agentTarget: undefined,
|
|
181932
|
+
check: false,
|
|
181933
|
+
dryRun: false,
|
|
181934
|
+
yes: false,
|
|
181935
|
+
force: false
|
|
181936
|
+
};
|
|
181937
|
+
for (let index = 0;index < args.length; index += 1) {
|
|
181938
|
+
const arg = args[index];
|
|
181939
|
+
if (arg === undefined) {
|
|
181940
|
+
continue;
|
|
180567
181941
|
}
|
|
180568
|
-
|
|
180569
|
-
|
|
180570
|
-
|
|
180571
|
-
|
|
181942
|
+
if (arg === "--check") {
|
|
181943
|
+
options.check = true;
|
|
181944
|
+
continue;
|
|
181945
|
+
}
|
|
181946
|
+
if (arg === "--dry-run") {
|
|
181947
|
+
options.dryRun = true;
|
|
181948
|
+
continue;
|
|
181949
|
+
}
|
|
181950
|
+
if (arg === "--yes") {
|
|
181951
|
+
options.yes = true;
|
|
181952
|
+
continue;
|
|
181953
|
+
}
|
|
181954
|
+
if (arg === "--force") {
|
|
181955
|
+
options.force = true;
|
|
181956
|
+
continue;
|
|
181957
|
+
}
|
|
181958
|
+
if (arg === "--root") {
|
|
181959
|
+
const root = args[index + 1];
|
|
181960
|
+
if (root === undefined) {
|
|
181961
|
+
throw new CliError("--root requires a path.", rootPathGuidance("upgrade"));
|
|
181962
|
+
}
|
|
181963
|
+
options.root = root;
|
|
181964
|
+
index += 1;
|
|
181965
|
+
continue;
|
|
181966
|
+
}
|
|
181967
|
+
if (arg === "--agent-target") {
|
|
181968
|
+
options.agentTarget = parseAgentTarget(args[index + 1]);
|
|
181969
|
+
index += 1;
|
|
181970
|
+
continue;
|
|
181971
|
+
}
|
|
181972
|
+
if (arg.startsWith("--")) {
|
|
181973
|
+
throw new CliError(`Unknown option: ${arg}`, commandHelpGuidance("upgrade"));
|
|
181974
|
+
}
|
|
181975
|
+
throw new CliError(`Unexpected argument: ${arg}`, commandHelpGuidance("upgrade"));
|
|
180572
181976
|
}
|
|
180573
|
-
|
|
180574
|
-
mkdirSync2(destinationDir, { recursive: true });
|
|
180575
|
-
cpSync(sourceDir, destinationDir, { recursive: true, force: true });
|
|
181977
|
+
return options;
|
|
180576
181978
|
}
|
|
180577
|
-
function
|
|
180578
|
-
const projectRoot =
|
|
181979
|
+
function runUpgrade(options, io, initRuntime, runtime) {
|
|
181980
|
+
const projectRoot = resolveProjectRoot2(options.root);
|
|
181981
|
+
const packageRoot = initRuntime.packageRoot ?? resolvePackageRoot();
|
|
181982
|
+
const plan = planUpgrade({
|
|
181983
|
+
projectRoot,
|
|
181984
|
+
packageRoot,
|
|
181985
|
+
currentVersion: runtime.currentVersion,
|
|
181986
|
+
latest: runtime.latest,
|
|
181987
|
+
guidance: detectUpgradeGuidance({
|
|
181988
|
+
packageRoot,
|
|
181989
|
+
projectRoot,
|
|
181990
|
+
...runtime.currentDirectory !== undefined ? { currentDirectory: runtime.currentDirectory } : {},
|
|
181991
|
+
...runtime.env !== undefined ? { env: runtime.env } : {}
|
|
181992
|
+
}),
|
|
181993
|
+
discovery: discoverRepository(projectRoot),
|
|
181994
|
+
options
|
|
181995
|
+
});
|
|
181996
|
+
if (plan.mode === "check" || options.dryRun) {
|
|
181997
|
+
io.stdout(formatUpgradePlan(plan));
|
|
181998
|
+
return plan.exitCode;
|
|
181999
|
+
}
|
|
182000
|
+
return applyUpgrade(projectRoot, packageRoot, plan, io, initRuntime);
|
|
182001
|
+
}
|
|
182002
|
+
function applyUpgrade(projectRoot, packageRoot, plan, io, initRuntime) {
|
|
182003
|
+
if (plan.requiresConfirmation && !initRuntime.prompts.isInteractive) {
|
|
182004
|
+
throw new CliError("Replacing or removing managed skill directories requires confirmation.", upgradeConfirmationGuidance());
|
|
182005
|
+
}
|
|
182006
|
+
if (plan.requiresConfirmation && !initRuntime.prompts.confirm(`Apply ${plan.operations.length} operation(s) to managed skill directories?`, false)) {
|
|
182007
|
+
io.stdout(formatUpgradePlan(plan));
|
|
182008
|
+
io.stdout(`No changes were applied.
|
|
182009
|
+
`);
|
|
182010
|
+
return 0;
|
|
182011
|
+
}
|
|
182012
|
+
for (const operation of plan.operations) {
|
|
182013
|
+
applySkillOperation(projectRoot, packageRoot, operation);
|
|
182014
|
+
}
|
|
182015
|
+
io.stdout(formatUpgradePlan(plan));
|
|
182016
|
+
return plan.exitCode;
|
|
182017
|
+
}
|
|
182018
|
+
function parseAgentTarget(value) {
|
|
182019
|
+
if (value === undefined) {
|
|
182020
|
+
throw new CliError("--agent-target requires a value.", agentTargetGuidance("upgrade"));
|
|
182021
|
+
}
|
|
182022
|
+
if (!AGENT_TARGETS2.has(value)) {
|
|
182023
|
+
throw new CliError(`Unknown agent target: ${value}. Supported values: codex, claude, both, none.`, agentTargetGuidance("upgrade"));
|
|
182024
|
+
}
|
|
182025
|
+
return value;
|
|
182026
|
+
}
|
|
182027
|
+
function resolveProjectRoot2(root) {
|
|
182028
|
+
const projectRoot = resolve4(root);
|
|
180579
182029
|
let stats;
|
|
180580
182030
|
try {
|
|
180581
|
-
stats =
|
|
182031
|
+
stats = statSync4(projectRoot);
|
|
180582
182032
|
} catch {
|
|
180583
|
-
throw new
|
|
182033
|
+
throw new CliError(`Root path does not exist: ${root}`, rootPathGuidance("upgrade"));
|
|
180584
182034
|
}
|
|
180585
182035
|
if (!stats.isDirectory()) {
|
|
180586
|
-
throw new
|
|
182036
|
+
throw new CliError(`Root path is not a directory: ${root}`, rootPathGuidance("upgrade"));
|
|
180587
182037
|
}
|
|
180588
182038
|
return projectRoot;
|
|
180589
182039
|
}
|
|
@@ -180602,11 +182052,11 @@ function parseContextOptions(args) {
|
|
|
180602
182052
|
function parseGraphOptions(args) {
|
|
180603
182053
|
return parseCommandOptions("graph", args);
|
|
180604
182054
|
}
|
|
180605
|
-
function
|
|
180606
|
-
const projectRoot =
|
|
182055
|
+
function resolveProjectRoot3(root, command) {
|
|
182056
|
+
const projectRoot = resolve5(root);
|
|
180607
182057
|
let stats;
|
|
180608
182058
|
try {
|
|
180609
|
-
stats =
|
|
182059
|
+
stats = statSync5(projectRoot);
|
|
180610
182060
|
} catch {
|
|
180611
182061
|
throw new CliError(`Root path does not exist: ${root}`, rootPathGuidance(command));
|
|
180612
182062
|
}
|
|
@@ -180616,7 +182066,7 @@ function resolveProjectRoot2(root, command) {
|
|
|
180616
182066
|
return projectRoot;
|
|
180617
182067
|
}
|
|
180618
182068
|
function runCheck(options, io) {
|
|
180619
|
-
const projectRoot =
|
|
182069
|
+
const projectRoot = resolveProjectRoot3(options.root, "check");
|
|
180620
182070
|
const result = check({ projectRoot, audit: options.audit });
|
|
180621
182071
|
if (options.json) {
|
|
180622
182072
|
io.stdout(`${JSON.stringify(result, null, 2)}
|
|
@@ -180630,8 +182080,8 @@ function runCheck(options, io) {
|
|
|
180630
182080
|
io.stdout(`${body}${formatSummary(result.summary)}
|
|
180631
182081
|
See \`docbridge docs show troubleshooting\` for diagnostic codes and fixes.
|
|
180632
182082
|
`);
|
|
180633
|
-
|
|
180634
|
-
|
|
182083
|
+
const guidance = repairGuidanceFor(result.diagnostics, projectRoot);
|
|
182084
|
+
if (guidance !== undefined) {
|
|
180635
182085
|
io.stderr(`${guidance}
|
|
180636
182086
|
`);
|
|
180637
182087
|
}
|
|
@@ -180639,10 +182089,10 @@ See \`docbridge docs show troubleshooting\` for diagnostic codes and fixes.
|
|
|
180639
182089
|
return result.summary.errors > 0 ? 1 : 0;
|
|
180640
182090
|
}
|
|
180641
182091
|
function runFileCommand(command, options, io, execute, render) {
|
|
180642
|
-
const projectRoot =
|
|
182092
|
+
const projectRoot = resolveProjectRoot3(options.root, command);
|
|
180643
182093
|
const inputFiles = [...options.files];
|
|
180644
182094
|
if (options.stdin) {
|
|
180645
|
-
const readStdin = io.stdin ?? (() =>
|
|
182095
|
+
const readStdin = io.stdin ?? (() => readFileSync9(0, "utf8"));
|
|
180646
182096
|
inputFiles.push(...readStdin().split(`
|
|
180647
182097
|
`));
|
|
180648
182098
|
}
|
|
@@ -180722,8 +182172,18 @@ function runGraph(options, io) {
|
|
|
180722
182172
|
return 0;
|
|
180723
182173
|
});
|
|
180724
182174
|
}
|
|
182175
|
+
function repairGuidanceFor(diagnostics, projectRoot) {
|
|
182176
|
+
const invalid = diagnostics.filter((diagnostic) => diagnostic.code === "config_file_invalid");
|
|
182177
|
+
if (invalid.some((diagnostic) => diagnostic.target === LINK_MANIFEST_FILE_NAME)) {
|
|
182178
|
+
return manifestRepairGuidance();
|
|
182179
|
+
}
|
|
182180
|
+
if (invalid.length === 0) {
|
|
182181
|
+
return;
|
|
182182
|
+
}
|
|
182183
|
+
return existsSync6(join14(projectRoot, CONFIG_FILE_NAME)) ? configRepairGuidance() : configSetupGuidance();
|
|
182184
|
+
}
|
|
180725
182185
|
function unknownCommandGuidance(command) {
|
|
180726
|
-
const suggestion =
|
|
182186
|
+
const suggestion = nearestMatch(command, SUBCOMMANDS);
|
|
180727
182187
|
const lines = ["Available commands:", ` ${SUBCOMMANDS.join(", ")}`];
|
|
180728
182188
|
if (suggestion !== undefined) {
|
|
180729
182189
|
lines.push("", `Did you mean \`${suggestion}\`?`);
|
|
@@ -180732,49 +182192,6 @@ function unknownCommandGuidance(command) {
|
|
|
180732
182192
|
return lines.join(`
|
|
180733
182193
|
`);
|
|
180734
182194
|
}
|
|
180735
|
-
function nearestSubcommand(input) {
|
|
180736
|
-
const ranked = SUBCOMMANDS.map((command, index) => ({
|
|
180737
|
-
command,
|
|
180738
|
-
distance: editDistance(input, command),
|
|
180739
|
-
index
|
|
180740
|
-
})).toSorted((left, right) => left.distance - right.distance || left.index - right.index);
|
|
180741
|
-
const abbreviation = ranked.find(({ command }) => isOrderedAbbreviation(input, command));
|
|
180742
|
-
if (abbreviation !== undefined) {
|
|
180743
|
-
return abbreviation.command;
|
|
180744
|
-
}
|
|
180745
|
-
const best = ranked[0];
|
|
180746
|
-
if (best === undefined) {
|
|
180747
|
-
return;
|
|
180748
|
-
}
|
|
180749
|
-
const closeEnough = best.distance <= Math.max(1, Math.floor(best.command.length / 2));
|
|
180750
|
-
return closeEnough ? best.command : undefined;
|
|
180751
|
-
}
|
|
180752
|
-
function isOrderedAbbreviation(input, command) {
|
|
180753
|
-
if (input.length < 3 || input.length >= command.length) {
|
|
180754
|
-
return false;
|
|
180755
|
-
}
|
|
180756
|
-
let commandIndex = 0;
|
|
180757
|
-
for (const character of input) {
|
|
180758
|
-
const matchIndex = command.indexOf(character, commandIndex);
|
|
180759
|
-
if (matchIndex === -1) {
|
|
180760
|
-
return false;
|
|
180761
|
-
}
|
|
180762
|
-
commandIndex = matchIndex + 1;
|
|
180763
|
-
}
|
|
180764
|
-
return true;
|
|
180765
|
-
}
|
|
180766
|
-
function editDistance(left, right) {
|
|
180767
|
-
let previous = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
180768
|
-
for (let leftIndex = 1;leftIndex <= left.length; leftIndex += 1) {
|
|
180769
|
-
const current = [leftIndex];
|
|
180770
|
-
for (let rightIndex = 1;rightIndex <= right.length; rightIndex += 1) {
|
|
180771
|
-
const substitutionCost = left[leftIndex - 1] === right[rightIndex - 1] ? 0 : 1;
|
|
180772
|
-
current[rightIndex] = Math.min((current[rightIndex - 1] ?? Number.POSITIVE_INFINITY) + 1, (previous[rightIndex] ?? Number.POSITIVE_INFINITY) + 1, (previous[rightIndex - 1] ?? Number.POSITIVE_INFINITY) + substitutionCost);
|
|
180773
|
-
}
|
|
180774
|
-
previous = current;
|
|
180775
|
-
}
|
|
180776
|
-
return previous[right.length] ?? 0;
|
|
180777
|
-
}
|
|
180778
182195
|
var COMMAND_HANDLERS = {
|
|
180779
182196
|
check: (args, io) => runCheck(parseCheckOptions(args), io),
|
|
180780
182197
|
related: (args, io) => runRelated(parseRelatedOptions(args), io),
|
|
@@ -180783,6 +182200,12 @@ var COMMAND_HANDLERS = {
|
|
|
180783
182200
|
docs: (args, io) => runDocs(parseDocsCommand(args), io),
|
|
180784
182201
|
init: (args, io, initRuntime) => runInit(parseInitOptions(args, "init"), io, initRuntime),
|
|
180785
182202
|
"init-with-agent": (args, io, initRuntime) => runInitWithAgent(parseInitOptions(args, "init-with-agent"), io, initRuntime),
|
|
182203
|
+
upgrade: (args, io, initRuntime, cliRuntime) => runUpgrade(parseUpgradeOptions(args), io, initRuntime, {
|
|
182204
|
+
latest: cliRuntime.latest ?? { status: "unavailable", source: "network" },
|
|
182205
|
+
currentVersion: VERSION,
|
|
182206
|
+
...cliRuntime.env !== undefined ? { env: cliRuntime.env } : {},
|
|
182207
|
+
...cliRuntime.currentDirectory !== undefined ? { currentDirectory: cliRuntime.currentDirectory } : {}
|
|
182208
|
+
}),
|
|
180786
182209
|
lsp: (args) => {
|
|
180787
182210
|
if (args.length > 0) {
|
|
180788
182211
|
throw new CliError("lsp takes no options.", commandHelpGuidance("lsp"));
|
|
@@ -180794,11 +182217,12 @@ var COMMAND_HANDLERS = {
|
|
|
180794
182217
|
function run(argv, io = {
|
|
180795
182218
|
stdout: (text) => process.stdout.write(text),
|
|
180796
182219
|
stderr: (text) => process.stderr.write(text)
|
|
180797
|
-
}, initRuntime = { prompts: createDefaultPrompts() }) {
|
|
182220
|
+
}, initRuntime = { prompts: createDefaultPrompts() }, cliRuntime = {}) {
|
|
180798
182221
|
const [command, ...rest] = argv;
|
|
180799
182222
|
try {
|
|
180800
182223
|
if (command === undefined || command === "--help" || command === "-h") {
|
|
180801
182224
|
io.stdout(GLOBAL_HELP);
|
|
182225
|
+
writeUpdateNotice(argv, io, initRuntime, cliRuntime);
|
|
180802
182226
|
return 0;
|
|
180803
182227
|
}
|
|
180804
182228
|
if (isSubcommand(command) && hasHelpFlag(rest)) {
|
|
@@ -180808,10 +182232,13 @@ function run(argv, io = {
|
|
|
180808
182232
|
if (command === "--version" || command === "-v") {
|
|
180809
182233
|
io.stdout(`${VERSION}
|
|
180810
182234
|
`);
|
|
182235
|
+
writeUpdateNotice(argv, io, initRuntime, cliRuntime);
|
|
180811
182236
|
return 0;
|
|
180812
182237
|
}
|
|
180813
182238
|
if (isSubcommand(command)) {
|
|
180814
|
-
|
|
182239
|
+
const exitCode = COMMAND_HANDLERS[command](rest, io, initRuntime, cliRuntime);
|
|
182240
|
+
writeUpdateNotice(argv, io, initRuntime, cliRuntime);
|
|
182241
|
+
return exitCode;
|
|
180815
182242
|
}
|
|
180816
182243
|
throw new CliError(`Unknown command: ${command}`, unknownCommandGuidance(command));
|
|
180817
182244
|
} catch (error) {
|
|
@@ -180825,8 +182252,59 @@ function run(argv, io = {
|
|
|
180825
182252
|
return 1;
|
|
180826
182253
|
}
|
|
180827
182254
|
}
|
|
182255
|
+
function rootOptionOf(argv, currentDirectory) {
|
|
182256
|
+
const flagIndex = argv.indexOf("--root");
|
|
182257
|
+
const value = flagIndex === -1 ? undefined : argv[flagIndex + 1];
|
|
182258
|
+
return value === undefined ? currentDirectory : resolve5(currentDirectory, value);
|
|
182259
|
+
}
|
|
182260
|
+
function writeUpdateNotice(argv, io, initRuntime, cliRuntime) {
|
|
182261
|
+
const latest = cliRuntime.latest;
|
|
182262
|
+
if (latest === undefined || latest.status !== "ok") {
|
|
182263
|
+
return;
|
|
182264
|
+
}
|
|
182265
|
+
if (!decideUpdateCheck({
|
|
182266
|
+
argv,
|
|
182267
|
+
...cliRuntime.env !== undefined ? { env: cliRuntime.env } : {},
|
|
182268
|
+
isTty: cliRuntime.isTty ?? false
|
|
182269
|
+
}).enabled) {
|
|
182270
|
+
return;
|
|
182271
|
+
}
|
|
182272
|
+
const packageRoot = initRuntime.packageRoot ?? resolvePackageRoot();
|
|
182273
|
+
const currentDirectory = cliRuntime.currentDirectory ?? process.cwd();
|
|
182274
|
+
const notice = formatUpdateNotice({
|
|
182275
|
+
current: VERSION,
|
|
182276
|
+
latest: latest.latest,
|
|
182277
|
+
guidance: detectUpgradeGuidance({
|
|
182278
|
+
packageRoot,
|
|
182279
|
+
projectRoot: rootOptionOf(argv, currentDirectory),
|
|
182280
|
+
currentDirectory,
|
|
182281
|
+
...cliRuntime.env !== undefined ? { env: cliRuntime.env } : {}
|
|
182282
|
+
})
|
|
182283
|
+
});
|
|
182284
|
+
if (notice !== undefined) {
|
|
182285
|
+
io.stderr(notice);
|
|
182286
|
+
}
|
|
182287
|
+
}
|
|
182288
|
+
async function resolveLatestForInvocation(argv, env, isTty) {
|
|
182289
|
+
if (isUpdateCheckOptedOut(env)) {
|
|
182290
|
+
return;
|
|
182291
|
+
}
|
|
182292
|
+
if (argv[0] === "upgrade" && !hasHelpFlag(argv.slice(1))) {
|
|
182293
|
+
return resolveLatestStableVersion({ forceRefresh: true });
|
|
182294
|
+
}
|
|
182295
|
+
if (!decideUpdateCheck({ argv, env, isTty }).enabled) {
|
|
182296
|
+
return;
|
|
182297
|
+
}
|
|
182298
|
+
return resolveLatestStableVersion();
|
|
182299
|
+
}
|
|
180828
182300
|
if (__require.main == __require.module) {
|
|
180829
|
-
|
|
182301
|
+
const argv = process.argv.slice(2);
|
|
182302
|
+
const isTty = process.stderr.isTTY === true;
|
|
182303
|
+
const latest = await resolveLatestForInvocation(argv, process.env, isTty);
|
|
182304
|
+
process.exitCode = run(argv, {
|
|
182305
|
+
stdout: (text) => process.stdout.write(text),
|
|
182306
|
+
stderr: (text) => process.stderr.write(text)
|
|
182307
|
+
}, { prompts: createDefaultPrompts() }, { ...latest !== undefined ? { latest } : {}, env: process.env, isTty });
|
|
180830
182308
|
}
|
|
180831
182309
|
export {
|
|
180832
182310
|
run,
|