project-librarian 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +21 -6
- package/README.md +14 -0
- package/dist/args.js +5 -2
- package/dist/code-index/evidence.js +79 -0
- package/dist/code-index/extractors/config.js +58 -0
- package/dist/code-index/extractors/light-languages.js +52 -0
- package/dist/code-index/extractors/registry.js +137 -0
- package/dist/code-index/extractors/shared.js +36 -0
- package/dist/code-index/extractors/tree-sitter.js +319 -0
- package/dist/code-index/extractors/types.js +2 -0
- package/dist/code-index/extractors/typescript.js +211 -0
- package/dist/code-index/incremental.js +16 -0
- package/dist/code-index/index-health.js +164 -0
- package/dist/code-index/modes.js +303 -0
- package/dist/code-index/ownership.js +183 -0
- package/dist/code-index/reports.js +322 -0
- package/dist/code-index/schema.js +196 -0
- package/dist/code-index/search.js +153 -0
- package/dist/code-index-file-policy.js +21 -27
- package/dist/code-index.js +167 -1850
- package/dist/init-project-wiki.js +38 -40
- package/dist/mcp-server.js +60 -6
- package/dist/migration.js +10 -6
- package/dist/modes.js +73 -42
- package/dist/path-ignore-policy.js +30 -0
- package/dist/wiki-corpus.js +25 -0
- package/dist/wiki-diagnostics.js +19 -0
- package/dist/wiki-files.js +2 -1
- package/dist/wiki-graph.js +1 -2
- package/package.json +9 -3
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.inspectCodeIndexHealth = inspectCodeIndexHealth;
|
|
37
|
+
exports.formatCodeIndexHealthRemediation = formatCodeIndexHealthRemediation;
|
|
38
|
+
const fs = __importStar(require("node:fs"));
|
|
39
|
+
function safeDiscoverCodeFiles(discoverCodeFiles, scopes) {
|
|
40
|
+
try {
|
|
41
|
+
return discoverCodeFiles(scopes).length;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function safeScalar(database, sql, param) {
|
|
48
|
+
try {
|
|
49
|
+
const rows = typeof param === "string" ? database.prepare(sql).all(param) : database.prepare(sql).all();
|
|
50
|
+
const value = rows[0]?.value;
|
|
51
|
+
return typeof value === "string" || typeof value === "number" ? String(value) : "";
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return "";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function tableNames(database) {
|
|
58
|
+
try {
|
|
59
|
+
return database.prepare("SELECT name AS value FROM sqlite_schema WHERE type IN ('table', 'view') ORDER BY name")
|
|
60
|
+
.all()
|
|
61
|
+
.map((row) => String(row.value))
|
|
62
|
+
.filter(Boolean);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function readScopes(scopesJson, scopesText, fallback) {
|
|
69
|
+
if (scopesJson) {
|
|
70
|
+
try {
|
|
71
|
+
const parsed = JSON.parse(scopesJson);
|
|
72
|
+
if (Array.isArray(parsed) && parsed.every((scope) => typeof scope === "string"))
|
|
73
|
+
return parsed;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// Fall back to legacy comma-separated metadata below.
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const scopes = scopesText.split(",").map((scope) => scope.trim()).filter(Boolean);
|
|
80
|
+
return scopes.length > 0 ? scopes : fallback;
|
|
81
|
+
}
|
|
82
|
+
function rebuildCommand(indexableFiles, smallRepoThreshold) {
|
|
83
|
+
const parts = ["project-librarian", "--code-index", "--code-index-full"];
|
|
84
|
+
if (indexableFiles !== null && indexableFiles < smallRepoThreshold)
|
|
85
|
+
parts.push("--acknowledge-small-repo");
|
|
86
|
+
return parts.join(" ");
|
|
87
|
+
}
|
|
88
|
+
function baseHealth(options, status, message, indexableFiles) {
|
|
89
|
+
return {
|
|
90
|
+
database_path: options.relativePath,
|
|
91
|
+
expected_schema_version: options.expectedSchemaVersion,
|
|
92
|
+
found_schema_version: "",
|
|
93
|
+
indexed_files: null,
|
|
94
|
+
indexable_files: indexableFiles,
|
|
95
|
+
message,
|
|
96
|
+
parser_mode: "",
|
|
97
|
+
recommended_rebuild_command: rebuildCommand(indexableFiles, options.smallRepoThreshold),
|
|
98
|
+
scopes: options.defaultScopes,
|
|
99
|
+
status,
|
|
100
|
+
tables: [],
|
|
101
|
+
updated_at: "",
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function inspectCodeIndexHealth(options) {
|
|
105
|
+
if (!fs.existsSync(options.absolutePath)) {
|
|
106
|
+
const indexableFiles = safeDiscoverCodeFiles(options.discoverCodeFiles, options.defaultScopes);
|
|
107
|
+
return baseHealth(options, "missing", `missing code evidence index: ${options.relativePath}`, indexableFiles);
|
|
108
|
+
}
|
|
109
|
+
let database;
|
|
110
|
+
try {
|
|
111
|
+
database = options.openDatabase(options.absolutePath);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
115
|
+
const indexableFiles = safeDiscoverCodeFiles(options.discoverCodeFiles, options.defaultScopes);
|
|
116
|
+
return baseHealth(options, "unreadable", `code evidence index is not readable: ${message}`, indexableFiles);
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
database.exec("PRAGMA query_only = ON");
|
|
120
|
+
const tables = tableNames(database);
|
|
121
|
+
const foundSchemaVersion = safeScalar(database, "SELECT value FROM meta WHERE key = ?", "schema_version");
|
|
122
|
+
const updatedAt = safeScalar(database, "SELECT value FROM meta WHERE key = ?", "updated_at");
|
|
123
|
+
const parserMode = safeScalar(database, "SELECT value FROM meta WHERE key = ?", "parser_mode");
|
|
124
|
+
const scopes = readScopes(safeScalar(database, "SELECT value FROM meta WHERE key = ?", "scopes_json"), safeScalar(database, "SELECT value FROM meta WHERE key = ?", "scopes"), options.defaultScopes);
|
|
125
|
+
const indexedFilesText = tables.includes("files") ? safeScalar(database, "SELECT count(*) AS value FROM files") : "";
|
|
126
|
+
const indexedFiles = indexedFilesText ? Number(indexedFilesText) : null;
|
|
127
|
+
const indexableFiles = safeDiscoverCodeFiles(options.discoverCodeFiles, scopes.length > 0 ? scopes : options.defaultScopes);
|
|
128
|
+
const compatible = foundSchemaVersion === options.expectedSchemaVersion;
|
|
129
|
+
return {
|
|
130
|
+
database_path: options.relativePath,
|
|
131
|
+
expected_schema_version: options.expectedSchemaVersion,
|
|
132
|
+
found_schema_version: foundSchemaVersion,
|
|
133
|
+
indexed_files: indexedFiles !== null && Number.isFinite(indexedFiles) ? indexedFiles : null,
|
|
134
|
+
indexable_files: indexableFiles,
|
|
135
|
+
message: compatible
|
|
136
|
+
? `code evidence index is compatible: schema ${foundSchemaVersion}`
|
|
137
|
+
: `code evidence index schema version ${foundSchemaVersion || "(missing)"} is incompatible with ${options.expectedSchemaVersion}`,
|
|
138
|
+
parser_mode: parserMode,
|
|
139
|
+
recommended_rebuild_command: rebuildCommand(indexableFiles, options.smallRepoThreshold),
|
|
140
|
+
scopes,
|
|
141
|
+
status: compatible ? "compatible" : "incompatible_schema",
|
|
142
|
+
tables,
|
|
143
|
+
updated_at: updatedAt,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
148
|
+
const indexableFiles = safeDiscoverCodeFiles(options.discoverCodeFiles, options.defaultScopes);
|
|
149
|
+
return baseHealth(options, "unreadable", `code evidence index is not readable: ${message}`, indexableFiles);
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
database.close();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function formatCodeIndexHealthRemediation(health) {
|
|
156
|
+
return [
|
|
157
|
+
health.message,
|
|
158
|
+
`database: ${health.database_path}`,
|
|
159
|
+
`status: ${health.status}`,
|
|
160
|
+
`expected_schema_version: ${health.expected_schema_version}`,
|
|
161
|
+
`found_schema_version: ${health.found_schema_version || "(missing)"}`,
|
|
162
|
+
`rebuild: ${health.recommended_rebuild_command}`,
|
|
163
|
+
].join("\n");
|
|
164
|
+
}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.runCodeIndexMode = runCodeIndexMode;
|
|
37
|
+
exports.runCodeQueryMode = runCodeQueryMode;
|
|
38
|
+
exports.runCodeReportMode = runCodeReportMode;
|
|
39
|
+
exports.runCodeStatusMode = runCodeStatusMode;
|
|
40
|
+
exports.runCodeIndexHealthMode = runCodeIndexHealthMode;
|
|
41
|
+
exports.runCodeFilesMode = runCodeFilesMode;
|
|
42
|
+
exports.runCodeImpactMode = runCodeImpactMode;
|
|
43
|
+
exports.runCodeContextPackMode = runCodeContextPackMode;
|
|
44
|
+
exports.runCodeSearchSymbolMode = runCodeSearchSymbolMode;
|
|
45
|
+
exports.isCodeEvidenceModeFor = isCodeEvidenceModeFor;
|
|
46
|
+
exports.isCodeEvidenceMode = isCodeEvidenceMode;
|
|
47
|
+
const fs = __importStar(require("node:fs"));
|
|
48
|
+
const args_1 = require("../args");
|
|
49
|
+
const code_index_file_policy_1 = require("../code-index-file-policy");
|
|
50
|
+
const code_index_sql_1 = require("../code-index-sql");
|
|
51
|
+
const reports_1 = require("./reports");
|
|
52
|
+
const schema_1 = require("./schema");
|
|
53
|
+
const search_1 = require("./search");
|
|
54
|
+
function printRows(rows) {
|
|
55
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
56
|
+
}
|
|
57
|
+
function printJson(value) {
|
|
58
|
+
console.log(JSON.stringify(value, null, 2));
|
|
59
|
+
}
|
|
60
|
+
function requireCompatibleDatabase(database, runtime) {
|
|
61
|
+
const schemaVersion = (0, schema_1.readMetaValue)(database, "schema_version");
|
|
62
|
+
if (schemaVersion !== schema_1.codeIndexSchemaVersion) {
|
|
63
|
+
runtime.fail(`code evidence index schema version ${schemaVersion || "(missing)"} is incompatible with ${schema_1.codeIndexSchemaVersion}; rebuild with --code-index`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function runCodeIndexMode(runtime) {
|
|
67
|
+
const databasePath = runtime.codeEvidenceDatabasePath();
|
|
68
|
+
const scopes = runtime.codeScopes();
|
|
69
|
+
const parserMode = runtime.selectedCodeParserMode();
|
|
70
|
+
// Scale gate before ANY write or database work: below the measured threshold
|
|
71
|
+
// the build halts with the evidence-citing warning unless --acknowledge-small-repo
|
|
72
|
+
// was passed (2026-06-12 scale-aware guidance decision).
|
|
73
|
+
const discoveredFiles = (0, code_index_file_policy_1.discoverCodeFiles)(scopes);
|
|
74
|
+
const scaleGate = (0, code_index_file_policy_1.smallRepoCodeIndexGate)(discoveredFiles.length, args_1.acknowledgeSmallRepoMode);
|
|
75
|
+
if (!scaleGate.proceed)
|
|
76
|
+
runtime.fail(scaleGate.warning);
|
|
77
|
+
const existingIndex = fs.existsSync(databasePath.absolutePath);
|
|
78
|
+
if (args_1.codeIndexIncrementalMode && !existingIndex) {
|
|
79
|
+
runtime.fail(`--incremental requires an existing compatible code evidence index: ${databasePath.relativePath}`);
|
|
80
|
+
}
|
|
81
|
+
let incremental = false;
|
|
82
|
+
if (existingIndex && !args_1.codeIndexFullMode) {
|
|
83
|
+
let compatibility = { compatible: false, reason: "compatibility was not checked" };
|
|
84
|
+
const existingDatabase = runtime.openDatabase(databasePath.absolutePath);
|
|
85
|
+
try {
|
|
86
|
+
compatibility = (0, schema_1.incrementalCompatibility)(existingDatabase, scopes, parserMode);
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
existingDatabase.close();
|
|
90
|
+
}
|
|
91
|
+
incremental = !args_1.codeIndexFullMode && compatibility.compatible;
|
|
92
|
+
if (args_1.codeIndexIncrementalMode && !compatibility.compatible)
|
|
93
|
+
runtime.fail(`--incremental cannot update ${databasePath.relativePath}: ${compatibility.reason}`);
|
|
94
|
+
}
|
|
95
|
+
runtime.prepareOutputPath();
|
|
96
|
+
if (!incremental)
|
|
97
|
+
runtime.removeDatabaseFiles(databasePath.absolutePath);
|
|
98
|
+
const database = runtime.openDatabase(databasePath.absolutePath);
|
|
99
|
+
try {
|
|
100
|
+
if (!incremental)
|
|
101
|
+
(0, schema_1.setupDatabase)(database);
|
|
102
|
+
const statements = (0, schema_1.createIndexStatements)(database);
|
|
103
|
+
const currentFingerprints = discoveredFiles.map((filePath) => runtime.readCodeFileFingerprint(filePath));
|
|
104
|
+
let reindexedFiles;
|
|
105
|
+
let deletedPaths;
|
|
106
|
+
let indexedPaths = new Set();
|
|
107
|
+
let unchangedFiles = 0;
|
|
108
|
+
if (incremental) {
|
|
109
|
+
const indexedRows = database.prepare("SELECT path, hash, mtime_ms, size FROM files").all();
|
|
110
|
+
indexedPaths = new Set(indexedRows.map((row) => String(row.path)));
|
|
111
|
+
const indexed = new Map(indexedRows.map((row) => [String(row.path), {
|
|
112
|
+
hash: String(row.hash),
|
|
113
|
+
mtimeMs: Number(row.mtime_ms),
|
|
114
|
+
size: Number(row.size),
|
|
115
|
+
}]));
|
|
116
|
+
const currentPaths = new Set(currentFingerprints.map((file) => file.path));
|
|
117
|
+
deletedPaths = indexedRows.map((row) => String(row.path)).filter((filePath) => !currentPaths.has(filePath));
|
|
118
|
+
reindexedFiles = [];
|
|
119
|
+
for (const file of currentFingerprints) {
|
|
120
|
+
const existing = indexed.get(file.path);
|
|
121
|
+
if (existing && existing.mtimeMs === file.mtimeMs && existing.size === file.size) {
|
|
122
|
+
unchangedFiles += 1;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
reindexedFiles.push(runtime.readCodeFile(file.path, parserMode));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
deletedPaths = [];
|
|
130
|
+
reindexedFiles = discoveredFiles.map((filePath) => runtime.readCodeFile(filePath, parserMode));
|
|
131
|
+
}
|
|
132
|
+
database.exec("BEGIN");
|
|
133
|
+
if (!incremental)
|
|
134
|
+
statements.insertMeta.run("created_at", new Date().toISOString());
|
|
135
|
+
(0, schema_1.writeIndexMetadata)(scopes, parserMode, statements);
|
|
136
|
+
for (const filePath of deletedPaths)
|
|
137
|
+
(0, schema_1.removeIndexedFile)(filePath, statements);
|
|
138
|
+
for (const file of reindexedFiles) {
|
|
139
|
+
if (incremental && indexedPaths.has(file.path))
|
|
140
|
+
(0, schema_1.removeIndexedFile)(file.path, statements);
|
|
141
|
+
runtime.indexCodeFile(file, statements);
|
|
142
|
+
}
|
|
143
|
+
database.exec("COMMIT");
|
|
144
|
+
console.log("Project wiki code evidence index complete.");
|
|
145
|
+
console.log(`database: ${databasePath.relativePath}`);
|
|
146
|
+
console.log(`mode: ${incremental ? "incremental" : "full"}`);
|
|
147
|
+
console.log(`parser_mode: ${parserMode}`);
|
|
148
|
+
console.log(`scopes: ${scopes.join(", ")}`);
|
|
149
|
+
console.log(`files: ${currentFingerprints.length}`);
|
|
150
|
+
console.log(`reindexed_files: ${reindexedFiles.length}`);
|
|
151
|
+
console.log(`deleted_files: ${deletedPaths.length}`);
|
|
152
|
+
console.log(`unchanged_files: ${unchangedFiles}`);
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
try {
|
|
156
|
+
database.exec("ROLLBACK");
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
// Ignore rollback failures after setup errors.
|
|
160
|
+
}
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
finally {
|
|
164
|
+
database.close();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function runCodeQueryMode(runtime) {
|
|
168
|
+
if (!args_1.codeQuerySql.trim()) {
|
|
169
|
+
console.error("missing SQL: use --code-query \"select ...\"");
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
runtime.requireExistingIndex();
|
|
173
|
+
if (!(0, code_index_sql_1.isReadOnlySql)(args_1.codeQuerySql)) {
|
|
174
|
+
console.error("code queries must be read-only SQL starting with SELECT or WITH");
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
const database = runtime.openDatabase(runtime.codeEvidenceDatabasePath().absolutePath);
|
|
178
|
+
try {
|
|
179
|
+
database.exec("PRAGMA query_only = ON");
|
|
180
|
+
requireCompatibleDatabase(database, runtime);
|
|
181
|
+
runtime.warnIfCodeIndexStale(database);
|
|
182
|
+
printRows(database.prepare(args_1.codeQuerySql).all());
|
|
183
|
+
}
|
|
184
|
+
finally {
|
|
185
|
+
database.close();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function runCodeReportMode(runtime) {
|
|
189
|
+
runtime.requireExistingIndex();
|
|
190
|
+
const database = runtime.openDatabase(runtime.codeEvidenceDatabasePath().absolutePath);
|
|
191
|
+
try {
|
|
192
|
+
requireCompatibleDatabase(database, runtime);
|
|
193
|
+
const staleness = runtime.codeIndexStaleness(database);
|
|
194
|
+
runtime.warnIfCodeIndexStale(database, staleness);
|
|
195
|
+
const report = runtime.codeReportForRequestedSection(database, args_1.codeReportSection, { staleness });
|
|
196
|
+
if (!report)
|
|
197
|
+
runtime.fail((0, reports_1.invalidCodeReportSectionMessage)(args_1.codeReportSection));
|
|
198
|
+
printJson(report);
|
|
199
|
+
}
|
|
200
|
+
finally {
|
|
201
|
+
database.close();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function runCodeStatusMode(runtime) {
|
|
205
|
+
runtime.requireExistingIndex();
|
|
206
|
+
const database = runtime.openDatabase(runtime.codeEvidenceDatabasePath().absolutePath);
|
|
207
|
+
try {
|
|
208
|
+
requireCompatibleDatabase(database, runtime);
|
|
209
|
+
const rows = database.prepare(`
|
|
210
|
+
SELECT 'files' AS metric, count(*) AS value FROM files
|
|
211
|
+
UNION ALL SELECT 'symbols', count(*) FROM symbols
|
|
212
|
+
UNION ALL SELECT 'imports', count(*) FROM imports
|
|
213
|
+
UNION ALL SELECT 'routes', count(*) FROM routes
|
|
214
|
+
UNION ALL SELECT 'edges', count(*) FROM edges
|
|
215
|
+
UNION ALL SELECT 'configs', count(*) FROM configs
|
|
216
|
+
`).all();
|
|
217
|
+
const staleness = runtime.codeIndexStaleness(database);
|
|
218
|
+
rows.push({ metric: "stale_files", value: staleness.added + staleness.changed + staleness.deleted }, { metric: "stale_changed_files", value: staleness.changed }, { metric: "stale_added_files", value: staleness.added }, { metric: "stale_deleted_files", value: staleness.deleted });
|
|
219
|
+
printRows(rows);
|
|
220
|
+
}
|
|
221
|
+
finally {
|
|
222
|
+
database.close();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function runCodeIndexHealthMode(runtime) {
|
|
226
|
+
printJson(runtime.codeIndexHealth());
|
|
227
|
+
}
|
|
228
|
+
function runCodeFilesMode(runtime) {
|
|
229
|
+
runtime.requireExistingIndex();
|
|
230
|
+
const database = runtime.openDatabase(runtime.codeEvidenceDatabasePath().absolutePath);
|
|
231
|
+
try {
|
|
232
|
+
requireCompatibleDatabase(database, runtime);
|
|
233
|
+
runtime.warnIfCodeIndexStale(database);
|
|
234
|
+
printRows(database.prepare("SELECT path, language, profile, kind, lines, bytes FROM files ORDER BY path").all());
|
|
235
|
+
}
|
|
236
|
+
finally {
|
|
237
|
+
database.close();
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
function runCodeImpactMode(runtime) {
|
|
241
|
+
if (!args_1.codeImpactTarget.trim()) {
|
|
242
|
+
console.error("missing impact target: use --code-impact \"path-or-symbol-or-module\"");
|
|
243
|
+
process.exit(1);
|
|
244
|
+
}
|
|
245
|
+
runtime.requireExistingIndex();
|
|
246
|
+
const database = runtime.openDatabase(runtime.codeEvidenceDatabasePath().absolutePath);
|
|
247
|
+
try {
|
|
248
|
+
requireCompatibleDatabase(database, runtime);
|
|
249
|
+
const staleness = runtime.codeIndexStaleness(database);
|
|
250
|
+
runtime.warnIfCodeIndexStale(database, staleness);
|
|
251
|
+
printJson(runtime.codeImpact(database, args_1.codeImpactTarget.trim(), { staleness }));
|
|
252
|
+
}
|
|
253
|
+
finally {
|
|
254
|
+
database.close();
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function runCodeContextPackMode(runtime) {
|
|
258
|
+
if (!args_1.codeContextPackTarget.trim()) {
|
|
259
|
+
console.error("missing context pack query: use --code-context-pack \"path-or-symbol-or-route\"");
|
|
260
|
+
process.exit(1);
|
|
261
|
+
}
|
|
262
|
+
runtime.requireExistingIndex();
|
|
263
|
+
const database = runtime.openDatabase(runtime.codeEvidenceDatabasePath().absolutePath);
|
|
264
|
+
try {
|
|
265
|
+
requireCompatibleDatabase(database, runtime);
|
|
266
|
+
const staleness = runtime.codeIndexStaleness(database);
|
|
267
|
+
runtime.warnIfCodeIndexStale(database, staleness);
|
|
268
|
+
console.log(runtime.codeContextPack(database, args_1.codeContextPackTarget.trim(), { staleness }));
|
|
269
|
+
}
|
|
270
|
+
finally {
|
|
271
|
+
database.close();
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
function runCodeSearchSymbolMode(runtime) {
|
|
275
|
+
if (!args_1.codeSearchSymbol.trim()) {
|
|
276
|
+
console.error("missing symbol search term: use --code-search-symbol \"term\"");
|
|
277
|
+
process.exit(1);
|
|
278
|
+
}
|
|
279
|
+
runtime.requireExistingIndex();
|
|
280
|
+
const database = runtime.openDatabase(runtime.codeEvidenceDatabasePath().absolutePath);
|
|
281
|
+
try {
|
|
282
|
+
requireCompatibleDatabase(database, runtime);
|
|
283
|
+
runtime.warnIfCodeIndexStale(database);
|
|
284
|
+
printRows((0, search_1.searchSymbols)(database, args_1.codeSearchSymbol.trim()));
|
|
285
|
+
}
|
|
286
|
+
finally {
|
|
287
|
+
database.close();
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
function isCodeEvidenceModeFor(flags) {
|
|
291
|
+
return Boolean(flags.codeContextPackTarget)
|
|
292
|
+
|| Boolean(flags.codeIndexHealthMode)
|
|
293
|
+
|| flags.codeIndexMode
|
|
294
|
+
|| Boolean(flags.codeQuerySql)
|
|
295
|
+
|| flags.codeReportMode
|
|
296
|
+
|| flags.codeStatusMode
|
|
297
|
+
|| flags.codeFilesMode
|
|
298
|
+
|| flags.codeImpactMode
|
|
299
|
+
|| Boolean(flags.codeSearchSymbol);
|
|
300
|
+
}
|
|
301
|
+
function isCodeEvidenceMode() {
|
|
302
|
+
return isCodeEvidenceModeFor({ codeContextPackTarget: args_1.codeContextPackTarget, codeFilesMode: args_1.codeFilesMode, codeImpactMode: args_1.codeImpactMode, codeIndexHealthMode: args_1.codeIndexHealthMode, codeIndexMode: args_1.codeIndexMode, codeQuerySql: args_1.codeQuerySql, codeReportMode: args_1.codeReportMode, codeSearchSymbol: args_1.codeSearchSymbol, codeStatusMode: args_1.codeStatusMode });
|
|
303
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.readJsonObject = readJsonObject;
|
|
37
|
+
exports.workspacePackages = workspacePackages;
|
|
38
|
+
exports.matchingWorkspace = matchingWorkspace;
|
|
39
|
+
exports.codeownerRules = codeownerRules;
|
|
40
|
+
exports.matchedCodeownerRules = matchedCodeownerRules;
|
|
41
|
+
exports.ownershipContext = ownershipContext;
|
|
42
|
+
exports.ownershipInfo = ownershipInfo;
|
|
43
|
+
const fs = __importStar(require("node:fs"));
|
|
44
|
+
const path = __importStar(require("node:path"));
|
|
45
|
+
const workspace_1 = require("../workspace");
|
|
46
|
+
function pathOwnerKey(filePath) {
|
|
47
|
+
const parts = (0, workspace_1.normalizePath)(filePath).split("/").filter(Boolean);
|
|
48
|
+
if (parts.length === 0)
|
|
49
|
+
return ".";
|
|
50
|
+
if (["apps", "libs", "packages", "services"].includes(parts[0] ?? "") && parts[1])
|
|
51
|
+
return `${parts[0]}/${parts[1]}`;
|
|
52
|
+
return parts[0] ?? ".";
|
|
53
|
+
}
|
|
54
|
+
function readJsonObject(relativePath) {
|
|
55
|
+
try {
|
|
56
|
+
const parsed = JSON.parse(fs.readFileSync((0, workspace_1.abs)(relativePath), "utf8"));
|
|
57
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function workspacePatternsFromRootPackage() {
|
|
64
|
+
const rootPackage = readJsonObject("package.json");
|
|
65
|
+
const workspaces = rootPackage?.workspaces;
|
|
66
|
+
if (Array.isArray(workspaces))
|
|
67
|
+
return workspaces.filter((value) => typeof value === "string");
|
|
68
|
+
if (workspaces && typeof workspaces === "object" && !Array.isArray(workspaces)) {
|
|
69
|
+
const packages = workspaces.packages;
|
|
70
|
+
if (Array.isArray(packages))
|
|
71
|
+
return packages.filter((value) => typeof value === "string");
|
|
72
|
+
}
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
function workspacePatternCandidates(pattern) {
|
|
76
|
+
const normalized = (0, workspace_1.normalizePath)(pattern).replace(/^\/+/, "").replace(/\/+$/, "");
|
|
77
|
+
if (!normalized || normalized.includes(".."))
|
|
78
|
+
return [];
|
|
79
|
+
if (!normalized.includes("*"))
|
|
80
|
+
return [normalized];
|
|
81
|
+
const starIndex = normalized.indexOf("*");
|
|
82
|
+
const prefix = normalized.slice(0, starIndex).replace(/\/+$/, "");
|
|
83
|
+
const suffix = normalized.slice(starIndex + 1).replace(/^\/+/, "");
|
|
84
|
+
const base = prefix || ".";
|
|
85
|
+
const basePath = (0, workspace_1.abs)(base);
|
|
86
|
+
if (!fs.existsSync(basePath) || !fs.statSync(basePath).isDirectory())
|
|
87
|
+
return [];
|
|
88
|
+
return fs.readdirSync(basePath, { withFileTypes: true })
|
|
89
|
+
.filter((entry) => entry.isDirectory())
|
|
90
|
+
.map((entry) => (0, workspace_1.normalizePath)(path.join(base, entry.name, suffix)))
|
|
91
|
+
.filter((candidate) => fs.existsSync((0, workspace_1.abs)(candidate)) && fs.statSync((0, workspace_1.abs)(candidate)).isDirectory());
|
|
92
|
+
}
|
|
93
|
+
function workspacePackages() {
|
|
94
|
+
const packages = new Map();
|
|
95
|
+
for (const pattern of workspacePatternsFromRootPackage()) {
|
|
96
|
+
for (const candidate of workspacePatternCandidates(pattern)) {
|
|
97
|
+
const packageJsonPath = (0, workspace_1.normalizePath)(path.join(candidate, "package.json"));
|
|
98
|
+
if (!fs.existsSync((0, workspace_1.abs)(packageJsonPath)))
|
|
99
|
+
continue;
|
|
100
|
+
const packageJson = readJsonObject(packageJsonPath);
|
|
101
|
+
const packageName = typeof packageJson?.name === "string" ? packageJson.name : candidate;
|
|
102
|
+
packages.set(candidate, {
|
|
103
|
+
name: packageName,
|
|
104
|
+
root: candidate,
|
|
105
|
+
source: "package.json workspaces",
|
|
106
|
+
workspace_pattern: pattern,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return Array.from(packages.values()).sort((left, right) => left.root.localeCompare(right.root));
|
|
111
|
+
}
|
|
112
|
+
function matchingWorkspace(filePath, workspaces) {
|
|
113
|
+
const normalized = (0, workspace_1.normalizePath)(filePath);
|
|
114
|
+
return workspaces
|
|
115
|
+
.filter((workspace) => normalized === workspace.root || normalized.startsWith(`${workspace.root}/`))
|
|
116
|
+
.sort((left, right) => right.root.length - left.root.length)[0] ?? null;
|
|
117
|
+
}
|
|
118
|
+
function codeownerRules() {
|
|
119
|
+
const files = [".github/CODEOWNERS", "CODEOWNERS", "docs/CODEOWNERS"];
|
|
120
|
+
const rules = [];
|
|
121
|
+
for (const filePath of files) {
|
|
122
|
+
if (!fs.existsSync((0, workspace_1.abs)(filePath)))
|
|
123
|
+
continue;
|
|
124
|
+
const lines = fs.readFileSync((0, workspace_1.abs)(filePath), "utf8").split(/\r?\n/);
|
|
125
|
+
lines.forEach((lineText, index) => {
|
|
126
|
+
const trimmed = lineText.trim();
|
|
127
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
128
|
+
return;
|
|
129
|
+
const parts = trimmed.split(/\s+/);
|
|
130
|
+
const pattern = parts[0] ?? "";
|
|
131
|
+
const owners = parts.slice(1);
|
|
132
|
+
if (!pattern || owners.length === 0)
|
|
133
|
+
return;
|
|
134
|
+
rules.push({ file_path: filePath, line: index + 1, owners, pattern });
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return rules;
|
|
138
|
+
}
|
|
139
|
+
function codeownerPatternRegex(pattern) {
|
|
140
|
+
const normalized = (0, workspace_1.normalizePath)(pattern).replace(/^\/+/, "");
|
|
141
|
+
const source = normalized
|
|
142
|
+
.replace(/[.+?^${}()|[\]\\]/g, "\\$&")
|
|
143
|
+
.replace(/\*\*/g, ".*")
|
|
144
|
+
.replace(/\*/g, "[^/]*");
|
|
145
|
+
if (normalized.endsWith("/"))
|
|
146
|
+
return new RegExp(`^${source}.*$`);
|
|
147
|
+
return new RegExp(`^${source}(?:/.*)?$`);
|
|
148
|
+
}
|
|
149
|
+
function codeownerPatternMatches(pattern, filePath) {
|
|
150
|
+
const normalized = (0, workspace_1.normalizePath)(pattern).replace(/^\/+/, "");
|
|
151
|
+
const target = (0, workspace_1.normalizePath)(filePath);
|
|
152
|
+
if (normalized === "*")
|
|
153
|
+
return true;
|
|
154
|
+
if (normalized.startsWith("*."))
|
|
155
|
+
return path.basename(target).endsWith(normalized.slice(1));
|
|
156
|
+
return codeownerPatternRegex(normalized).test(target);
|
|
157
|
+
}
|
|
158
|
+
function matchingCodeowners(filePath, rules) {
|
|
159
|
+
const matches = rules.filter((rule) => codeownerPatternMatches(rule.pattern, filePath));
|
|
160
|
+
return matches[matches.length - 1]?.owners ?? [];
|
|
161
|
+
}
|
|
162
|
+
// Return every CODEOWNERS rule that matches a path, in file order. The last entry
|
|
163
|
+
// is the effective owner under last-match-wins; earlier entries are overridden.
|
|
164
|
+
// Reuses the same matcher as matchingCodeowners so precedence answers stay
|
|
165
|
+
// consistent with --code-report / --code-impact.
|
|
166
|
+
function matchedCodeownerRules(filePath, rules) {
|
|
167
|
+
return rules.filter((rule) => codeownerPatternMatches(rule.pattern, filePath));
|
|
168
|
+
}
|
|
169
|
+
function ownershipContext() {
|
|
170
|
+
return {
|
|
171
|
+
codeownerRules: codeownerRules(),
|
|
172
|
+
workspaces: workspacePackages(),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function ownershipInfo(filePath, context) {
|
|
176
|
+
const workspace = matchingWorkspace(filePath, context.workspaces);
|
|
177
|
+
const owners = matchingCodeowners(filePath, context.codeownerRules);
|
|
178
|
+
return {
|
|
179
|
+
codeowners: owners.join(", "),
|
|
180
|
+
owner: workspace?.root ?? pathOwnerKey(filePath),
|
|
181
|
+
owner_source: workspace ? "workspace" : "path",
|
|
182
|
+
};
|
|
183
|
+
}
|