@pratik7368patil/anchor-core 0.1.22 → 0.1.24
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/dist/index.d.ts +154 -2
- package/dist/index.js +442 -75
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1541,10 +1541,17 @@ function defaultDatabasePath(cwd) {
|
|
|
1541
1541
|
function openAnchorDatabase(cwd, databasePath = defaultDatabasePath(cwd)) {
|
|
1542
1542
|
fs3.mkdirSync(path4.dirname(databasePath), { recursive: true });
|
|
1543
1543
|
const db = new Database(databasePath);
|
|
1544
|
+
db.pragma("busy_timeout = 5000");
|
|
1544
1545
|
db.pragma("journal_mode = WAL");
|
|
1545
1546
|
db.pragma("foreign_keys = ON");
|
|
1546
1547
|
return db;
|
|
1547
1548
|
}
|
|
1549
|
+
function openAnchorDatabaseReadOnly(databasePath) {
|
|
1550
|
+
const db = new Database(databasePath, { readonly: true, fileMustExist: true });
|
|
1551
|
+
db.pragma("busy_timeout = 5000");
|
|
1552
|
+
db.pragma("foreign_keys = ON");
|
|
1553
|
+
return db;
|
|
1554
|
+
}
|
|
1548
1555
|
function initializeSchema(db) {
|
|
1549
1556
|
db.exec(SCHEMA_SQL);
|
|
1550
1557
|
ensureColumn(db, "sync_state", "history_coverage", "TEXT");
|
|
@@ -3101,6 +3108,18 @@ function indexCodebase(db, options) {
|
|
|
3101
3108
|
architecture
|
|
3102
3109
|
);
|
|
3103
3110
|
refreshTestCommands(db, options.cwd, options.repo);
|
|
3111
|
+
options.onProgress?.({
|
|
3112
|
+
stage: "completed_code_index",
|
|
3113
|
+
repo: options.repo,
|
|
3114
|
+
files: summary.indexedFiles,
|
|
3115
|
+
chunks: summary.codeChunksCreated,
|
|
3116
|
+
skippedFiles: summary.skippedFiles,
|
|
3117
|
+
testFiles: summary.testFilesIndexed,
|
|
3118
|
+
testLinks: summary.testLinksCreated,
|
|
3119
|
+
architectureComponents: summary.architectureComponentsIndexed,
|
|
3120
|
+
architecturePatterns: summary.architecturePatternsIndexed,
|
|
3121
|
+
architectureImports: summary.architectureImportsIndexed
|
|
3122
|
+
});
|
|
3104
3123
|
return summary;
|
|
3105
3124
|
}
|
|
3106
3125
|
function emptyCodeIndexSummary(cwd) {
|
|
@@ -3425,7 +3444,8 @@ function indexPullRequests(db, pullRequests, options) {
|
|
|
3425
3444
|
current: index + 1,
|
|
3426
3445
|
total: pullRequests.length,
|
|
3427
3446
|
prNumber: pr.number,
|
|
3428
|
-
wisdomUnitsCreated: result.wisdom
|
|
3447
|
+
wisdomUnitsCreated: result.wisdom,
|
|
3448
|
+
regressionEventsCreated: result.regressions
|
|
3429
3449
|
});
|
|
3430
3450
|
}
|
|
3431
3451
|
if (options.updateSyncStateAfter !== false) {
|
|
@@ -6292,6 +6312,18 @@ function parseGraphQLResponse(text, status, headers) {
|
|
|
6292
6312
|
);
|
|
6293
6313
|
}
|
|
6294
6314
|
}
|
|
6315
|
+
function sleep2(ms) {
|
|
6316
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
6317
|
+
}
|
|
6318
|
+
function isTransientGraphQLError(error) {
|
|
6319
|
+
const status = error.status;
|
|
6320
|
+
if (status === 502 || status === 503 || status === 504) return true;
|
|
6321
|
+
const message = (error.message ?? "").toLowerCase();
|
|
6322
|
+
return message.includes("fetch failed") || message.includes("econnreset") || message.includes("etimedout") || message.includes("socket hang up") || message.includes("network") || message.includes("non-json response") && (message.includes("text/html") || message.includes("<!doctype") || message.includes("<html") || typeof status === "number" && status >= 500);
|
|
6323
|
+
}
|
|
6324
|
+
function transientRetryDelayMs(attempt) {
|
|
6325
|
+
return Math.min(4e3, 500 * 2 ** Math.max(0, attempt - 1));
|
|
6326
|
+
}
|
|
6295
6327
|
function createGitHubGraphQLRequester(options) {
|
|
6296
6328
|
if (!options.token.trim()) {
|
|
6297
6329
|
throw new Error(
|
|
@@ -6303,36 +6335,55 @@ function createGitHubGraphQLRequester(options) {
|
|
|
6303
6335
|
return async function requestGitHubGraphQL(query, variables, requestOptions) {
|
|
6304
6336
|
return requestWithGitHubRateLimit(
|
|
6305
6337
|
async () => {
|
|
6306
|
-
const
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
|
|
6318
|
-
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
|
|
6322
|
-
|
|
6323
|
-
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
|
|
6327
|
-
|
|
6328
|
-
|
|
6338
|
+
const maxAttempts = Math.max(1, requestOptions.maxTransientRetries ?? 3);
|
|
6339
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
6340
|
+
try {
|
|
6341
|
+
const response = await fetchImpl("https://api.github.com/graphql", {
|
|
6342
|
+
method: "POST",
|
|
6343
|
+
headers: {
|
|
6344
|
+
accept: "application/vnd.github+json",
|
|
6345
|
+
authorization: `Bearer ${options.token}`,
|
|
6346
|
+
"content-type": "application/json",
|
|
6347
|
+
"user-agent": "anchor-local-mcp"
|
|
6348
|
+
},
|
|
6349
|
+
body: JSON.stringify({ query, variables })
|
|
6350
|
+
});
|
|
6351
|
+
const headers = headersToRecord(response.headers);
|
|
6352
|
+
const raw = parseGraphQLResponse(await response.text(), response.status, headers);
|
|
6353
|
+
if (!response.ok || raw.errors?.length) {
|
|
6354
|
+
throw new GitHubGraphQLError(errorMessage(response.status, raw.errors), {
|
|
6355
|
+
status: errorStatus(response.status, raw.errors),
|
|
6356
|
+
headers
|
|
6357
|
+
});
|
|
6358
|
+
}
|
|
6359
|
+
if (!raw.data) {
|
|
6360
|
+
throw new GitHubGraphQLError("GitHub GraphQL response did not include data.", {
|
|
6361
|
+
status: response.status,
|
|
6362
|
+
headers
|
|
6363
|
+
});
|
|
6364
|
+
}
|
|
6365
|
+
updateGitHubGraphQLRateLimitState(
|
|
6366
|
+
requestOptions.controller,
|
|
6367
|
+
raw.data.rateLimit,
|
|
6368
|
+
requestOptions.requestName
|
|
6369
|
+
);
|
|
6370
|
+
return { data: raw.data, headers };
|
|
6371
|
+
} catch (error) {
|
|
6372
|
+
if (attempt >= maxAttempts || !isTransientGraphQLError(error)) throw error;
|
|
6373
|
+
const waitMs = transientRetryDelayMs(attempt);
|
|
6374
|
+
requestOptions.onTransientRetry?.({
|
|
6375
|
+
attempt,
|
|
6376
|
+
maxAttempts,
|
|
6377
|
+
waitMs,
|
|
6378
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
6379
|
+
});
|
|
6380
|
+
await sleep2(waitMs);
|
|
6381
|
+
}
|
|
6329
6382
|
}
|
|
6330
|
-
|
|
6331
|
-
|
|
6332
|
-
|
|
6333
|
-
|
|
6334
|
-
);
|
|
6335
|
-
return { data: raw.data, headers };
|
|
6383
|
+
throw new GitHubGraphQLError("GitHub GraphQL request retry loop exited unexpectedly.", {
|
|
6384
|
+
status: 500,
|
|
6385
|
+
headers: {}
|
|
6386
|
+
});
|
|
6336
6387
|
},
|
|
6337
6388
|
{
|
|
6338
6389
|
controller: requestOptions.controller,
|
|
@@ -6518,6 +6569,16 @@ var GraphQLBudget = class {
|
|
|
6518
6569
|
};
|
|
6519
6570
|
}
|
|
6520
6571
|
};
|
|
6572
|
+
function graphqlRetryProgress(repo, onProgress) {
|
|
6573
|
+
return (retry) => onProgress?.({
|
|
6574
|
+
stage: "github_graphql_retry",
|
|
6575
|
+
repo,
|
|
6576
|
+
attempt: retry.attempt,
|
|
6577
|
+
maxAttempts: retry.maxAttempts,
|
|
6578
|
+
waitMs: retry.waitMs,
|
|
6579
|
+
reason: retry.reason
|
|
6580
|
+
});
|
|
6581
|
+
}
|
|
6521
6582
|
var PULL_REQUEST_FIELDS = `
|
|
6522
6583
|
number
|
|
6523
6584
|
url
|
|
@@ -6655,7 +6716,8 @@ function connectionNodes(connection) {
|
|
|
6655
6716
|
async function requestGraphQLWithBudget(requestGraphQL, query, variables, options) {
|
|
6656
6717
|
const response = await requestGraphQL(query, variables, {
|
|
6657
6718
|
controller: options.controller,
|
|
6658
|
-
requestName: options.requestName
|
|
6719
|
+
requestName: options.requestName,
|
|
6720
|
+
onTransientRetry: options.onTransientRetry
|
|
6659
6721
|
});
|
|
6660
6722
|
options.budget.observe(response.data.rateLimit);
|
|
6661
6723
|
return response;
|
|
@@ -6741,7 +6803,8 @@ async function appendAdditionalFiles(requestGraphQL, record, initialConnection,
|
|
|
6741
6803
|
{
|
|
6742
6804
|
controller: options.controller,
|
|
6743
6805
|
requestName: `GraphQL /repos/${record.repo}/pulls/${record.number}/files`,
|
|
6744
|
-
budget: options.budget
|
|
6806
|
+
budget: options.budget,
|
|
6807
|
+
onTransientRetry: options.onTransientRetry
|
|
6745
6808
|
}
|
|
6746
6809
|
);
|
|
6747
6810
|
record.files.push(
|
|
@@ -6767,7 +6830,8 @@ async function appendAdditionalIssueComments(requestGraphQL, record, initialConn
|
|
|
6767
6830
|
{
|
|
6768
6831
|
controller: options.controller,
|
|
6769
6832
|
requestName: `GraphQL /repos/${record.repo}/issues/${record.number}/comments`,
|
|
6770
|
-
budget: options.budget
|
|
6833
|
+
budget: options.budget,
|
|
6834
|
+
onTransientRetry: options.onTransientRetry
|
|
6771
6835
|
}
|
|
6772
6836
|
);
|
|
6773
6837
|
record.issueComments?.push(...connectionNodes(connection).map(mapIssueComment));
|
|
@@ -6791,7 +6855,8 @@ async function appendAdditionalCommits(requestGraphQL, record, initialConnection
|
|
|
6791
6855
|
{
|
|
6792
6856
|
controller: options.controller,
|
|
6793
6857
|
requestName: `GraphQL /repos/${record.repo}/pulls/${record.number}/commits`,
|
|
6794
|
-
budget: options.budget
|
|
6858
|
+
budget: options.budget,
|
|
6859
|
+
onTransientRetry: options.onTransientRetry
|
|
6795
6860
|
}
|
|
6796
6861
|
);
|
|
6797
6862
|
record.commits?.push(
|
|
@@ -6816,7 +6881,8 @@ async function appendAdditionalReviewComments(requestGraphQL, record, review, op
|
|
|
6816
6881
|
{
|
|
6817
6882
|
controller: options.controller,
|
|
6818
6883
|
requestName: `GraphQL /pull-request-reviews/${review.id}/comments`,
|
|
6819
|
-
budget: options.budget
|
|
6884
|
+
budget: options.budget,
|
|
6885
|
+
onTransientRetry: options.onTransientRetry
|
|
6820
6886
|
}
|
|
6821
6887
|
);
|
|
6822
6888
|
const connection = response.data.node?.comments;
|
|
@@ -6842,7 +6908,8 @@ async function appendAdditionalReviews(requestGraphQL, record, initialConnection
|
|
|
6842
6908
|
{
|
|
6843
6909
|
controller: options.controller,
|
|
6844
6910
|
requestName: `GraphQL /repos/${record.repo}/pulls/${record.number}/reviews`,
|
|
6845
|
-
budget: options.budget
|
|
6911
|
+
budget: options.budget,
|
|
6912
|
+
onTransientRetry: options.onTransientRetry
|
|
6846
6913
|
}
|
|
6847
6914
|
);
|
|
6848
6915
|
const reviewNodes = connectionNodes(connection);
|
|
@@ -6856,7 +6923,8 @@ async function appendAdditionalReviews(requestGraphQL, record, initialConnection
|
|
|
6856
6923
|
for (const review of reviewsToHydrate) {
|
|
6857
6924
|
await appendAdditionalReviewComments(requestGraphQL, record, review, {
|
|
6858
6925
|
controller: options.controller,
|
|
6859
|
-
budget: options.budget
|
|
6926
|
+
budget: options.budget,
|
|
6927
|
+
onTransientRetry: options.onTransientRetry
|
|
6860
6928
|
});
|
|
6861
6929
|
}
|
|
6862
6930
|
}
|
|
@@ -7012,6 +7080,7 @@ async function fetchMergedPullRequestsWithGraphQL(options) {
|
|
|
7012
7080
|
checkpoint?.pageSize ?? Math.min(INITIAL_PULL_REQUEST_PAGE_SIZE, options.limit ?? INITIAL_PULL_REQUEST_PAGE_SIZE)
|
|
7013
7081
|
);
|
|
7014
7082
|
const budget = new GraphQLBudget(GRAPHQL_RATE_LIMIT_RESERVE);
|
|
7083
|
+
const onTransientRetry = graphqlRetryProgress(options.repo, options.onProgress);
|
|
7015
7084
|
const checkpointScope = checkpoint?.scope ?? `${options.repo}|${options.limit === void 0 ? "all" : `limit:${options.limit}`}|since:${options.since ?? ""}`;
|
|
7016
7085
|
options.onProgress?.({
|
|
7017
7086
|
stage: "discovering_pull_requests",
|
|
@@ -7038,7 +7107,8 @@ async function fetchMergedPullRequestsWithGraphQL(options) {
|
|
|
7038
7107
|
{
|
|
7039
7108
|
controller: options.controller,
|
|
7040
7109
|
requestName: "GraphQL rate limit preflight",
|
|
7041
|
-
budget
|
|
7110
|
+
budget,
|
|
7111
|
+
onTransientRetry
|
|
7042
7112
|
}
|
|
7043
7113
|
);
|
|
7044
7114
|
const preflightRateLimit = budget.rateLimit();
|
|
@@ -7100,7 +7170,8 @@ async function fetchMergedPullRequestsWithGraphQL(options) {
|
|
|
7100
7170
|
{
|
|
7101
7171
|
controller: options.controller,
|
|
7102
7172
|
requestName: `GraphQL /repos/${options.repo}/pullRequests`,
|
|
7103
|
-
budget
|
|
7173
|
+
budget,
|
|
7174
|
+
onTransientRetry
|
|
7104
7175
|
}
|
|
7105
7176
|
);
|
|
7106
7177
|
} catch (error) {
|
|
@@ -7131,7 +7202,8 @@ async function fetchMergedPullRequestsWithGraphQL(options) {
|
|
|
7131
7202
|
owner,
|
|
7132
7203
|
name,
|
|
7133
7204
|
controller: options.controller,
|
|
7134
|
-
budget
|
|
7205
|
+
budget,
|
|
7206
|
+
onTransientRetry
|
|
7135
7207
|
});
|
|
7136
7208
|
records.push(record);
|
|
7137
7209
|
if (options.limit !== void 0 && records.length >= options.limit) break;
|
|
@@ -7553,6 +7625,9 @@ function openOrgDatabase(org, baseDir) {
|
|
|
7553
7625
|
initializeSchema(db);
|
|
7554
7626
|
return db;
|
|
7555
7627
|
}
|
|
7628
|
+
function openOrgDatabaseReadOnly(org, baseDir) {
|
|
7629
|
+
return openAnchorDatabaseReadOnly(orgDatabasePath(org, baseDir));
|
|
7630
|
+
}
|
|
7556
7631
|
function syncOrgConfigToDatabase(db, config, baseDir) {
|
|
7557
7632
|
initializeSchema(db);
|
|
7558
7633
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -7696,6 +7771,21 @@ function recordOrgGraphState(db, input) {
|
|
|
7696
7771
|
now
|
|
7697
7772
|
);
|
|
7698
7773
|
}
|
|
7774
|
+
function getOrgGraphState(db, org) {
|
|
7775
|
+
initializeSchema(db);
|
|
7776
|
+
const row = db.prepare("SELECT * FROM org_graph_state WHERE org = ?").get(org);
|
|
7777
|
+
if (!row) return void 0;
|
|
7778
|
+
return {
|
|
7779
|
+
org: row.org,
|
|
7780
|
+
lastBuiltAt: row.last_built_at ?? void 0,
|
|
7781
|
+
lastStatus: row.last_status ?? void 0,
|
|
7782
|
+
lastDurationMs: row.last_duration_ms ?? void 0,
|
|
7783
|
+
edgeCount: row.edge_count ?? void 0,
|
|
7784
|
+
apiContractCount: row.api_contract_count ?? void 0,
|
|
7785
|
+
apiConsumerCount: row.api_consumer_count ?? void 0,
|
|
7786
|
+
lastError: row.last_error ?? void 0
|
|
7787
|
+
};
|
|
7788
|
+
}
|
|
7699
7789
|
function count(db, table, where = "", params = []) {
|
|
7700
7790
|
const row = db.prepare(`SELECT COUNT(*) AS count FROM ${table} ${where}`).get(...params);
|
|
7701
7791
|
return row.count;
|
|
@@ -7715,9 +7805,11 @@ function grade(score) {
|
|
|
7715
7805
|
if (score < 80) return "good";
|
|
7716
7806
|
return "excellent";
|
|
7717
7807
|
}
|
|
7718
|
-
function getOrgStatus(db, config, baseDir) {
|
|
7719
|
-
|
|
7720
|
-
|
|
7808
|
+
function getOrgStatus(db, config, baseDir, options = {}) {
|
|
7809
|
+
if (options.syncConfig !== false) {
|
|
7810
|
+
initializeSchema(db);
|
|
7811
|
+
syncOrgConfigToDatabase(db, config, baseDir);
|
|
7812
|
+
}
|
|
7721
7813
|
const enabledRepos = config.repos.filter((repo) => repo.enabled);
|
|
7722
7814
|
const states = new Map(
|
|
7723
7815
|
db.prepare("SELECT * FROM org_repo_state WHERE org = ?").all(config.org).map((row) => [row.repo, row])
|
|
@@ -7764,6 +7856,8 @@ function getOrgStatus(db, config, baseDir) {
|
|
|
7764
7856
|
org: config.org,
|
|
7765
7857
|
root: orgRoot(config.org, baseDir),
|
|
7766
7858
|
databasePath: orgDatabasePath(config.org, baseDir),
|
|
7859
|
+
statusReadError: options.statusReadError,
|
|
7860
|
+
activeRun: options.activeRun,
|
|
7767
7861
|
repoCount: config.repos.length,
|
|
7768
7862
|
enabledRepoCount: enabledRepos.length,
|
|
7769
7863
|
clonedRepoCount,
|
|
@@ -7798,10 +7892,84 @@ function getOrgStatus(db, config, baseDir) {
|
|
|
7798
7892
|
};
|
|
7799
7893
|
}
|
|
7800
7894
|
|
|
7801
|
-
// src/org/
|
|
7802
|
-
import { execFileSync as execFileSync4 } from "child_process";
|
|
7895
|
+
// src/org/heartbeat.ts
|
|
7803
7896
|
import fs11 from "fs";
|
|
7804
7897
|
import path21 from "path";
|
|
7898
|
+
var HEARTBEAT_STALE_AFTER_MS = 2 * 60 * 1e3;
|
|
7899
|
+
function orgHeartbeatPath(org, baseDir) {
|
|
7900
|
+
return path21.join(orgRoot(validateOrgName(org), baseDir), "sync-heartbeat.json");
|
|
7901
|
+
}
|
|
7902
|
+
function atomicWriteJson2(filePath, value) {
|
|
7903
|
+
fs11.mkdirSync(path21.dirname(filePath), { recursive: true });
|
|
7904
|
+
const tmp = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
7905
|
+
fs11.writeFileSync(tmp, `${JSON.stringify(value, null, 2)}
|
|
7906
|
+
`, { mode: 384 });
|
|
7907
|
+
fs11.renameSync(tmp, filePath);
|
|
7908
|
+
}
|
|
7909
|
+
function processIsRunning(pid) {
|
|
7910
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
7911
|
+
try {
|
|
7912
|
+
process.kill(pid, 0);
|
|
7913
|
+
return true;
|
|
7914
|
+
} catch {
|
|
7915
|
+
return false;
|
|
7916
|
+
}
|
|
7917
|
+
}
|
|
7918
|
+
function parseHeartbeat(value) {
|
|
7919
|
+
if (!value || typeof value !== "object") return void 0;
|
|
7920
|
+
const candidate = value;
|
|
7921
|
+
if (typeof candidate.pid !== "number" || typeof candidate.command !== "string" || typeof candidate.org !== "string" || typeof candidate.phase !== "string" || typeof candidate.startedAt !== "string" || typeof candidate.updatedAt !== "string") {
|
|
7922
|
+
return void 0;
|
|
7923
|
+
}
|
|
7924
|
+
return {
|
|
7925
|
+
pid: candidate.pid,
|
|
7926
|
+
command: candidate.command,
|
|
7927
|
+
org: candidate.org,
|
|
7928
|
+
repo: typeof candidate.repo === "string" ? candidate.repo : void 0,
|
|
7929
|
+
repoIndex: typeof candidate.repoIndex === "number" ? candidate.repoIndex : void 0,
|
|
7930
|
+
repoTotal: typeof candidate.repoTotal === "number" ? candidate.repoTotal : void 0,
|
|
7931
|
+
phase: candidate.phase,
|
|
7932
|
+
startedAt: candidate.startedAt,
|
|
7933
|
+
updatedAt: candidate.updatedAt
|
|
7934
|
+
};
|
|
7935
|
+
}
|
|
7936
|
+
function writeOrgHeartbeat(heartbeat, baseDir) {
|
|
7937
|
+
atomicWriteJson2(orgHeartbeatPath(heartbeat.org, baseDir), heartbeat);
|
|
7938
|
+
}
|
|
7939
|
+
function clearOrgHeartbeat(org, baseDir) {
|
|
7940
|
+
try {
|
|
7941
|
+
const filePath = orgHeartbeatPath(org, baseDir);
|
|
7942
|
+
if (fs11.existsSync(filePath)) fs11.unlinkSync(filePath);
|
|
7943
|
+
} catch {
|
|
7944
|
+
}
|
|
7945
|
+
}
|
|
7946
|
+
function readOrgHeartbeat(org, baseDir) {
|
|
7947
|
+
const filePath = orgHeartbeatPath(org, baseDir);
|
|
7948
|
+
if (!fs11.existsSync(filePath)) return void 0;
|
|
7949
|
+
try {
|
|
7950
|
+
const heartbeat = parseHeartbeat(JSON.parse(fs11.readFileSync(filePath, "utf8")));
|
|
7951
|
+
if (!heartbeat) return void 0;
|
|
7952
|
+
const now = Date.now();
|
|
7953
|
+
const startedAtMs = Date.parse(heartbeat.startedAt);
|
|
7954
|
+
const updatedAtMs = Date.parse(heartbeat.updatedAt);
|
|
7955
|
+
const pidRunning = processIsRunning(heartbeat.pid);
|
|
7956
|
+
const lastUpdateAgeSeconds = Number.isFinite(updatedAtMs) ? Math.max(0, Math.floor((now - updatedAtMs) / 1e3)) : 0;
|
|
7957
|
+
return {
|
|
7958
|
+
...heartbeat,
|
|
7959
|
+
pidRunning,
|
|
7960
|
+
stale: !pidRunning || !Number.isFinite(updatedAtMs) || now - updatedAtMs > HEARTBEAT_STALE_AFTER_MS,
|
|
7961
|
+
elapsedSeconds: Number.isFinite(startedAtMs) ? Math.max(0, Math.floor((now - startedAtMs) / 1e3)) : 0,
|
|
7962
|
+
lastUpdateAgeSeconds
|
|
7963
|
+
};
|
|
7964
|
+
} catch {
|
|
7965
|
+
return void 0;
|
|
7966
|
+
}
|
|
7967
|
+
}
|
|
7968
|
+
|
|
7969
|
+
// src/org/clone.ts
|
|
7970
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
7971
|
+
import fs12 from "fs";
|
|
7972
|
+
import path22 from "path";
|
|
7805
7973
|
function defaultGitCommandRunner(command, args, options = {}) {
|
|
7806
7974
|
return execFileSync4(command, args, {
|
|
7807
7975
|
cwd: options.cwd,
|
|
@@ -7817,7 +7985,7 @@ function currentCommit(runner, localPath) {
|
|
|
7817
7985
|
}
|
|
7818
7986
|
}
|
|
7819
7987
|
function plannedOrgCloneCommands(repo, localPath) {
|
|
7820
|
-
if (!
|
|
7988
|
+
if (!fs12.existsSync(path22.join(localPath, ".git"))) {
|
|
7821
7989
|
return [
|
|
7822
7990
|
{
|
|
7823
7991
|
command: "git",
|
|
@@ -7846,8 +8014,8 @@ function plannedOrgCloneCommands(repo, localPath) {
|
|
|
7846
8014
|
function cloneOrPullOrgRepo(input) {
|
|
7847
8015
|
const runner = input.runner ?? defaultGitCommandRunner;
|
|
7848
8016
|
const localPath = orgRepoLocalPath(input.org, input.repo, input.baseDir);
|
|
7849
|
-
const existed =
|
|
7850
|
-
|
|
8017
|
+
const existed = fs12.existsSync(path22.join(localPath, ".git"));
|
|
8018
|
+
fs12.mkdirSync(path22.dirname(localPath), { recursive: true });
|
|
7851
8019
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7852
8020
|
try {
|
|
7853
8021
|
const commands = plannedOrgCloneCommands(input.repo, localPath);
|
|
@@ -7948,8 +8116,8 @@ function orgCloneStateFromResult(org, repo, result) {
|
|
|
7948
8116
|
|
|
7949
8117
|
// src/org/graph.ts
|
|
7950
8118
|
import crypto9 from "crypto";
|
|
7951
|
-
import
|
|
7952
|
-
import
|
|
8119
|
+
import fs13 from "fs";
|
|
8120
|
+
import path23 from "path";
|
|
7953
8121
|
function stableId(parts) {
|
|
7954
8122
|
return crypto9.createHash("sha256").update(parts.join("\0")).digest("hex").slice(0, 32);
|
|
7955
8123
|
}
|
|
@@ -7963,10 +8131,10 @@ function fileEvidence(repo, filePath, note) {
|
|
|
7963
8131
|
};
|
|
7964
8132
|
}
|
|
7965
8133
|
function readPackageManifest(repoPath) {
|
|
7966
|
-
const packagePath =
|
|
7967
|
-
if (!
|
|
8134
|
+
const packagePath = path23.join(repoPath, "package.json");
|
|
8135
|
+
if (!fs13.existsSync(packagePath)) return void 0;
|
|
7968
8136
|
try {
|
|
7969
|
-
return JSON.parse(
|
|
8137
|
+
return JSON.parse(fs13.readFileSync(packagePath, "utf8"));
|
|
7970
8138
|
} catch {
|
|
7971
8139
|
return void 0;
|
|
7972
8140
|
}
|
|
@@ -8259,7 +8427,11 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
8259
8427
|
const insertEdge = db.prepare(
|
|
8260
8428
|
`INSERT INTO org_cross_repo_edges
|
|
8261
8429
|
(id, org, source_repo, source_path, target_repo, target_path, relationship, evidence_json, confidence, created_at)
|
|
8262
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
8430
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
8431
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
8432
|
+
evidence_json = excluded.evidence_json,
|
|
8433
|
+
confidence = excluded.confidence,
|
|
8434
|
+
created_at = excluded.created_at`
|
|
8263
8435
|
);
|
|
8264
8436
|
for (const edge of edges) {
|
|
8265
8437
|
insertEdge.run(
|
|
@@ -8278,7 +8450,12 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
8278
8450
|
const insertContract = db.prepare(
|
|
8279
8451
|
`INSERT INTO org_api_contracts
|
|
8280
8452
|
(id, org, repo, file_path, contract, evidence_json, confidence, created_at)
|
|
8281
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
8453
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
8454
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
8455
|
+
contract = excluded.contract,
|
|
8456
|
+
evidence_json = excluded.evidence_json,
|
|
8457
|
+
confidence = excluded.confidence,
|
|
8458
|
+
created_at = excluded.created_at`
|
|
8282
8459
|
);
|
|
8283
8460
|
for (const contract of apiContracts) {
|
|
8284
8461
|
insertContract.run(
|
|
@@ -8295,7 +8472,12 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
8295
8472
|
const insertConsumer = db.prepare(
|
|
8296
8473
|
`INSERT INTO org_api_consumers
|
|
8297
8474
|
(id, org, provider_repo, provider_path, consumer_repo, consumer_path, contract, evidence_json, confidence, created_at)
|
|
8298
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
8475
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
8476
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
8477
|
+
contract = excluded.contract,
|
|
8478
|
+
evidence_json = excluded.evidence_json,
|
|
8479
|
+
confidence = excluded.confidence,
|
|
8480
|
+
created_at = excluded.created_at`
|
|
8299
8481
|
);
|
|
8300
8482
|
for (const consumer of apiConsumers) {
|
|
8301
8483
|
insertConsumer.run(
|
|
@@ -8358,7 +8540,8 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
8358
8540
|
}
|
|
8359
8541
|
|
|
8360
8542
|
// src/org/index.ts
|
|
8361
|
-
import
|
|
8543
|
+
import fs14 from "fs";
|
|
8544
|
+
var ORG_SYNC_RESUME_WINDOW_MS = 12 * 60 * 60 * 1e3;
|
|
8362
8545
|
function readCommit(runner, cwd) {
|
|
8363
8546
|
try {
|
|
8364
8547
|
return runner("git", ["rev-parse", "HEAD"], { cwd });
|
|
@@ -8369,6 +8552,27 @@ function readCommit(runner, cwd) {
|
|
|
8369
8552
|
function missingCloneError(repo, localPath) {
|
|
8370
8553
|
return `Repo ${repo} is not cloned at ${localPath}. Run anchor org clone --repo ${repo} --org <org>.`;
|
|
8371
8554
|
}
|
|
8555
|
+
function latestIsoDate(dates) {
|
|
8556
|
+
return dates.filter((date) => Boolean(date)).sort().at(-1);
|
|
8557
|
+
}
|
|
8558
|
+
function graphIsFreshForState(input) {
|
|
8559
|
+
const latestRepoIndexAt = latestIsoDate([input.lastPrSyncAt, input.lastCodeIndexedAt]);
|
|
8560
|
+
return Boolean(
|
|
8561
|
+
latestRepoIndexAt && input.graphStatus === "success" && input.graphBuiltAt && input.graphBuiltAt >= latestRepoIndexAt
|
|
8562
|
+
);
|
|
8563
|
+
}
|
|
8564
|
+
function isWithinResumeWindow(date) {
|
|
8565
|
+
const parsed = Date.parse(date);
|
|
8566
|
+
return Number.isFinite(parsed) && Date.now() - parsed <= ORG_SYNC_RESUME_WINDOW_MS;
|
|
8567
|
+
}
|
|
8568
|
+
function shouldSkipPrFetchForResume(input) {
|
|
8569
|
+
if (input.options.command !== "org sync") return false;
|
|
8570
|
+
if (input.options.force || input.options.since || input.options.noGraph) return false;
|
|
8571
|
+
if (input.options.codeOnly || input.options.prsOnly) return false;
|
|
8572
|
+
if (!input.lastPrSyncAt) return false;
|
|
8573
|
+
if (!isWithinResumeWindow(input.lastPrSyncAt)) return false;
|
|
8574
|
+
return !graphIsFreshForState(input);
|
|
8575
|
+
}
|
|
8372
8576
|
async function indexOrgRepos(db, config, options = {}) {
|
|
8373
8577
|
initializeSchema(db);
|
|
8374
8578
|
syncOrgConfigToDatabase(db, config, options.baseDir);
|
|
@@ -8376,30 +8580,94 @@ async function indexOrgRepos(db, config, options = {}) {
|
|
|
8376
8580
|
(repo) => repo.enabled && (!options.repo || repo.fullName === options.repo)
|
|
8377
8581
|
);
|
|
8378
8582
|
const runner = options.runner ?? defaultGitCommandRunner;
|
|
8583
|
+
const fetchPullRequests = options.fetchPullRequests ?? fetchMergedPullRequests;
|
|
8379
8584
|
const auth = options.token ? { token: options.token } : resolveGitHubToken();
|
|
8380
8585
|
const results = [];
|
|
8381
8586
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8382
|
-
|
|
8587
|
+
const startedAtMs = Date.now();
|
|
8588
|
+
const graphState = getOrgGraphState(db, config.org);
|
|
8589
|
+
const command = options.command ?? "org index";
|
|
8590
|
+
const emit = (progress) => options.onLifecycleProgress?.(progress);
|
|
8591
|
+
emit({
|
|
8592
|
+
stage: "org_sync_started",
|
|
8593
|
+
org: config.org,
|
|
8594
|
+
command,
|
|
8595
|
+
totalRepos: repos.length
|
|
8596
|
+
});
|
|
8597
|
+
for (const [repoIndex, repo] of repos.entries()) {
|
|
8598
|
+
const repoPosition = repoIndex + 1;
|
|
8383
8599
|
const localPath = orgRepoLocalPath(config.org, repo, options.baseDir);
|
|
8384
8600
|
const repoStartedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8601
|
+
const repoStartedAtMs = Date.now();
|
|
8385
8602
|
let prsIndexed = 0;
|
|
8386
8603
|
let codeFilesIndexed = 0;
|
|
8387
8604
|
try {
|
|
8388
|
-
|
|
8605
|
+
emit({
|
|
8606
|
+
stage: "org_repo_started",
|
|
8607
|
+
org: config.org,
|
|
8608
|
+
command,
|
|
8609
|
+
repo: repo.fullName,
|
|
8610
|
+
current: repoPosition,
|
|
8611
|
+
total: repos.length
|
|
8612
|
+
});
|
|
8613
|
+
if (!fs14.existsSync(localPath)) throw new Error(missingCloneError(repo.fullName, localPath));
|
|
8614
|
+
emit({
|
|
8615
|
+
stage: "org_repo_phase",
|
|
8616
|
+
org: config.org,
|
|
8617
|
+
command,
|
|
8618
|
+
repo: repo.fullName,
|
|
8619
|
+
current: repoPosition,
|
|
8620
|
+
total: repos.length,
|
|
8621
|
+
phase: "Reading current commit"
|
|
8622
|
+
});
|
|
8389
8623
|
const currentCommit2 = readCommit(runner, localPath);
|
|
8390
8624
|
const state = getOrgRepoState(db, config.org, repo.fullName);
|
|
8391
8625
|
let history;
|
|
8392
8626
|
let code;
|
|
8627
|
+
let skippedHistory = false;
|
|
8628
|
+
let historySkippedReason;
|
|
8393
8629
|
const repoFailures = [];
|
|
8394
8630
|
if (!options.codeOnly) {
|
|
8395
|
-
if (
|
|
8631
|
+
if (shouldSkipPrFetchForResume({
|
|
8632
|
+
options,
|
|
8633
|
+
lastPrSyncAt: state?.lastPrSyncAt,
|
|
8634
|
+
lastCodeIndexedAt: state?.lastCodeIndexedAt,
|
|
8635
|
+
graphBuiltAt: graphState?.lastBuiltAt,
|
|
8636
|
+
graphStatus: graphState?.lastStatus
|
|
8637
|
+
})) {
|
|
8638
|
+
skippedHistory = true;
|
|
8639
|
+
historySkippedReason = "PR history already synced; resuming unfinished org graph/index work.";
|
|
8640
|
+
emit({
|
|
8641
|
+
stage: "org_repo_skipped_history",
|
|
8642
|
+
org: config.org,
|
|
8643
|
+
command,
|
|
8644
|
+
repo: repo.fullName,
|
|
8645
|
+
current: repoPosition,
|
|
8646
|
+
total: repos.length,
|
|
8647
|
+
reason: historySkippedReason
|
|
8648
|
+
});
|
|
8649
|
+
options.onFetchProgress?.({
|
|
8650
|
+
stage: "skipped_pull_request_fetch",
|
|
8651
|
+
repo: repo.fullName,
|
|
8652
|
+
reason: historySkippedReason
|
|
8653
|
+
});
|
|
8654
|
+
} else if (!auth.token) {
|
|
8396
8655
|
repoFailures.push(
|
|
8397
8656
|
"GitHub authentication is required for org PR indexing. Run gh auth login, or export GITHUB_TOKEN/GH_TOKEN with read-only access."
|
|
8398
8657
|
);
|
|
8399
8658
|
} else {
|
|
8400
8659
|
try {
|
|
8660
|
+
emit({
|
|
8661
|
+
stage: "org_repo_phase",
|
|
8662
|
+
org: config.org,
|
|
8663
|
+
command,
|
|
8664
|
+
repo: repo.fullName,
|
|
8665
|
+
current: repoPosition,
|
|
8666
|
+
total: repos.length,
|
|
8667
|
+
phase: "Fetching PR history"
|
|
8668
|
+
});
|
|
8401
8669
|
const since = options.since ?? (options.command === "org sync" ? state?.lastPrSyncAt ?? getLastSyncTime(db, repo.fullName) : void 0);
|
|
8402
|
-
const pullRequests = await
|
|
8670
|
+
const pullRequests = await fetchPullRequests({
|
|
8403
8671
|
token: auth.token,
|
|
8404
8672
|
repo: repo.fullName,
|
|
8405
8673
|
limit: 200,
|
|
@@ -8407,6 +8675,16 @@ async function indexOrgRepos(db, config, options = {}) {
|
|
|
8407
8675
|
detailConcurrency: options.concurrency,
|
|
8408
8676
|
onProgress: options.onFetchProgress
|
|
8409
8677
|
});
|
|
8678
|
+
emit({
|
|
8679
|
+
stage: "org_repo_phase",
|
|
8680
|
+
org: config.org,
|
|
8681
|
+
command,
|
|
8682
|
+
repo: repo.fullName,
|
|
8683
|
+
current: repoPosition,
|
|
8684
|
+
total: repos.length,
|
|
8685
|
+
phase: "Indexing PR history into SQLite",
|
|
8686
|
+
detail: `${pullRequests.length} PR(s)`
|
|
8687
|
+
});
|
|
8410
8688
|
history = indexPullRequests(db, pullRequests, {
|
|
8411
8689
|
cwd: localPath,
|
|
8412
8690
|
repo: repo.fullName,
|
|
@@ -8431,6 +8709,15 @@ async function indexOrgRepos(db, config, options = {}) {
|
|
|
8431
8709
|
}
|
|
8432
8710
|
const codeUnchanged = !options.force && currentCommit2 && state?.lastCodeIndexedCommit && currentCommit2 === state.lastCodeIndexedCommit;
|
|
8433
8711
|
if (!options.prsOnly && !codeUnchanged) {
|
|
8712
|
+
emit({
|
|
8713
|
+
stage: "org_repo_phase",
|
|
8714
|
+
org: config.org,
|
|
8715
|
+
command,
|
|
8716
|
+
repo: repo.fullName,
|
|
8717
|
+
current: repoPosition,
|
|
8718
|
+
total: repos.length,
|
|
8719
|
+
phase: "Indexing code and architecture"
|
|
8720
|
+
});
|
|
8434
8721
|
code = indexCodebase(db, {
|
|
8435
8722
|
cwd: localPath,
|
|
8436
8723
|
repo: repo.fullName,
|
|
@@ -8446,6 +8733,26 @@ async function indexOrgRepos(db, config, options = {}) {
|
|
|
8446
8733
|
lastCodeIndexedCommit: currentCommit2,
|
|
8447
8734
|
lastCodeIndexedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
8448
8735
|
});
|
|
8736
|
+
} else if (!options.prsOnly && codeUnchanged) {
|
|
8737
|
+
emit({
|
|
8738
|
+
stage: "org_repo_skipped_code",
|
|
8739
|
+
org: config.org,
|
|
8740
|
+
command,
|
|
8741
|
+
repo: repo.fullName,
|
|
8742
|
+
current: repoPosition,
|
|
8743
|
+
total: repos.length,
|
|
8744
|
+
reason: "Code skipped: current commit already indexed."
|
|
8745
|
+
});
|
|
8746
|
+
} else if (options.prsOnly) {
|
|
8747
|
+
emit({
|
|
8748
|
+
stage: "org_repo_skipped_code",
|
|
8749
|
+
org: config.org,
|
|
8750
|
+
command,
|
|
8751
|
+
repo: repo.fullName,
|
|
8752
|
+
current: repoPosition,
|
|
8753
|
+
total: repos.length,
|
|
8754
|
+
reason: "Code skipped because --prs-only was passed."
|
|
8755
|
+
});
|
|
8449
8756
|
}
|
|
8450
8757
|
if (repoFailures.length > 0) {
|
|
8451
8758
|
updateOrgRepoState(db, {
|
|
@@ -8457,9 +8764,19 @@ async function indexOrgRepos(db, config, options = {}) {
|
|
|
8457
8764
|
lastError: repoFailures.join("; ")
|
|
8458
8765
|
});
|
|
8459
8766
|
}
|
|
8767
|
+
emit({
|
|
8768
|
+
stage: "org_repo_finalizing",
|
|
8769
|
+
org: config.org,
|
|
8770
|
+
command,
|
|
8771
|
+
repo: repo.fullName,
|
|
8772
|
+
current: repoPosition,
|
|
8773
|
+
total: repos.length
|
|
8774
|
+
});
|
|
8460
8775
|
results.push({
|
|
8461
8776
|
repo: repo.fullName,
|
|
8462
8777
|
skippedCode: Boolean(codeUnchanged || options.prsOnly),
|
|
8778
|
+
skippedHistory,
|
|
8779
|
+
historySkippedReason,
|
|
8463
8780
|
currentCommit: currentCommit2,
|
|
8464
8781
|
history,
|
|
8465
8782
|
code,
|
|
@@ -8468,7 +8785,7 @@ async function indexOrgRepos(db, config, options = {}) {
|
|
|
8468
8785
|
recordOrgIndexRun(db, {
|
|
8469
8786
|
org: config.org,
|
|
8470
8787
|
repo: repo.fullName,
|
|
8471
|
-
command
|
|
8788
|
+
command,
|
|
8472
8789
|
startedAt: repoStartedAt,
|
|
8473
8790
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8474
8791
|
status: repoFailures.length > 0 ? "partial" : "success",
|
|
@@ -8476,6 +8793,20 @@ async function indexOrgRepos(db, config, options = {}) {
|
|
|
8476
8793
|
codeFilesIndexed,
|
|
8477
8794
|
failures: repoFailures
|
|
8478
8795
|
});
|
|
8796
|
+
emit({
|
|
8797
|
+
stage: "org_repo_completed",
|
|
8798
|
+
org: config.org,
|
|
8799
|
+
command,
|
|
8800
|
+
repo: repo.fullName,
|
|
8801
|
+
current: repoPosition,
|
|
8802
|
+
total: repos.length,
|
|
8803
|
+
skippedHistory,
|
|
8804
|
+
skippedCode: Boolean(codeUnchanged || options.prsOnly),
|
|
8805
|
+
prsIndexed,
|
|
8806
|
+
codeFilesIndexed,
|
|
8807
|
+
durationMs: Date.now() - repoStartedAtMs,
|
|
8808
|
+
error: repoFailures.join("; ") || void 0
|
|
8809
|
+
});
|
|
8479
8810
|
} catch (error) {
|
|
8480
8811
|
const message = error instanceof Error ? error.message : String(error);
|
|
8481
8812
|
updateOrgRepoState(db, {
|
|
@@ -8488,7 +8819,7 @@ async function indexOrgRepos(db, config, options = {}) {
|
|
|
8488
8819
|
recordOrgIndexRun(db, {
|
|
8489
8820
|
org: config.org,
|
|
8490
8821
|
repo: repo.fullName,
|
|
8491
|
-
command
|
|
8822
|
+
command,
|
|
8492
8823
|
startedAt: repoStartedAt,
|
|
8493
8824
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8494
8825
|
status: "failed",
|
|
@@ -8499,6 +8830,20 @@ async function indexOrgRepos(db, config, options = {}) {
|
|
|
8499
8830
|
skippedCode: false,
|
|
8500
8831
|
error: message
|
|
8501
8832
|
});
|
|
8833
|
+
emit({
|
|
8834
|
+
stage: "org_repo_completed",
|
|
8835
|
+
org: config.org,
|
|
8836
|
+
command,
|
|
8837
|
+
repo: repo.fullName,
|
|
8838
|
+
current: repoPosition,
|
|
8839
|
+
total: repos.length,
|
|
8840
|
+
skippedHistory: false,
|
|
8841
|
+
skippedCode: false,
|
|
8842
|
+
prsIndexed,
|
|
8843
|
+
codeFilesIndexed,
|
|
8844
|
+
durationMs: Date.now() - repoStartedAtMs,
|
|
8845
|
+
error: message
|
|
8846
|
+
});
|
|
8502
8847
|
}
|
|
8503
8848
|
}
|
|
8504
8849
|
let graph;
|
|
@@ -8511,6 +8856,12 @@ async function indexOrgRepos(db, config, options = {}) {
|
|
|
8511
8856
|
apiContractCount: counts.apiContracts,
|
|
8512
8857
|
apiConsumerCount: counts.apiConsumers
|
|
8513
8858
|
});
|
|
8859
|
+
emit({
|
|
8860
|
+
stage: "org_graph_skipped",
|
|
8861
|
+
org: config.org,
|
|
8862
|
+
command,
|
|
8863
|
+
reason: "Graph skipped because --no-graph was passed."
|
|
8864
|
+
});
|
|
8514
8865
|
graph = { ...counts, skipped: true };
|
|
8515
8866
|
} else {
|
|
8516
8867
|
try {
|
|
@@ -8531,7 +8882,7 @@ async function indexOrgRepos(db, config, options = {}) {
|
|
|
8531
8882
|
}
|
|
8532
8883
|
recordOrgIndexRun(db, {
|
|
8533
8884
|
org: config.org,
|
|
8534
|
-
command
|
|
8885
|
+
command,
|
|
8535
8886
|
startedAt,
|
|
8536
8887
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8537
8888
|
status: results.some((result) => result.error) || graph.error ? "partial" : "success",
|
|
@@ -8539,6 +8890,15 @@ async function indexOrgRepos(db, config, options = {}) {
|
|
|
8539
8890
|
codeFilesIndexed: results.reduce((sum, result) => sum + (result.code?.indexedFiles ?? 0), 0),
|
|
8540
8891
|
failures: results.map((result) => result.error).concat(graph.error ? [graph.error] : []).filter((error) => Boolean(error))
|
|
8541
8892
|
});
|
|
8893
|
+
emit({
|
|
8894
|
+
stage: "org_sync_completed",
|
|
8895
|
+
org: config.org,
|
|
8896
|
+
command,
|
|
8897
|
+
totalRepos: repos.length,
|
|
8898
|
+
succeededRepos: results.filter((result) => !result.error).length,
|
|
8899
|
+
failedRepos: results.filter((result) => result.error).length,
|
|
8900
|
+
durationMs: Date.now() - startedAtMs
|
|
8901
|
+
});
|
|
8542
8902
|
return {
|
|
8543
8903
|
org: config.org,
|
|
8544
8904
|
repos: results.sort((a, b) => a.repo.localeCompare(b.repo)),
|
|
@@ -9163,8 +9523,8 @@ function isTestPath2(filePath) {
|
|
|
9163
9523
|
}
|
|
9164
9524
|
|
|
9165
9525
|
// src/doctor.ts
|
|
9166
|
-
import
|
|
9167
|
-
import
|
|
9526
|
+
import fs15 from "fs";
|
|
9527
|
+
import path24 from "path";
|
|
9168
9528
|
function check(name, ok, message, fix) {
|
|
9169
9529
|
return { name, ok, message, fix: ok ? void 0 : fix };
|
|
9170
9530
|
}
|
|
@@ -9268,12 +9628,12 @@ async function runDoctor(options) {
|
|
|
9268
9628
|
)
|
|
9269
9629
|
);
|
|
9270
9630
|
}
|
|
9271
|
-
const cursorConfigPath =
|
|
9631
|
+
const cursorConfigPath = path24.join(gitRoot ?? cwd, ".cursor", "mcp.json");
|
|
9272
9632
|
let cursorConfig;
|
|
9273
9633
|
let cursorConfigValid = false;
|
|
9274
|
-
if (
|
|
9634
|
+
if (fs15.existsSync(cursorConfigPath)) {
|
|
9275
9635
|
try {
|
|
9276
|
-
cursorConfig = JSON.parse(
|
|
9636
|
+
cursorConfig = JSON.parse(fs15.readFileSync(cursorConfigPath, "utf8"));
|
|
9277
9637
|
cursorConfigValid = true;
|
|
9278
9638
|
} catch {
|
|
9279
9639
|
cursorConfigValid = false;
|
|
@@ -9282,7 +9642,7 @@ async function runDoctor(options) {
|
|
|
9282
9642
|
checks.push(
|
|
9283
9643
|
check(
|
|
9284
9644
|
".cursor/mcp.json valid",
|
|
9285
|
-
|
|
9645
|
+
fs15.existsSync(cursorConfigPath) && cursorConfigValid,
|
|
9286
9646
|
cursorConfigValid ? ".cursor/mcp.json exists and is valid JSON." : ".cursor/mcp.json is missing or invalid.",
|
|
9287
9647
|
"Run anchor init. If the file is malformed, fix the JSON and rerun anchor init."
|
|
9288
9648
|
)
|
|
@@ -9299,7 +9659,7 @@ async function runDoctor(options) {
|
|
|
9299
9659
|
)
|
|
9300
9660
|
);
|
|
9301
9661
|
const dbPath = defaultDatabasePath(gitRoot ?? cwd);
|
|
9302
|
-
const dbExists =
|
|
9662
|
+
const dbExists = fs15.existsSync(dbPath);
|
|
9303
9663
|
checks.push(
|
|
9304
9664
|
check(
|
|
9305
9665
|
".anchor/index.sqlite exists",
|
|
@@ -9343,12 +9703,12 @@ async function runDoctor(options) {
|
|
|
9343
9703
|
"Run pnpm build, then try anchor serve from the repository."
|
|
9344
9704
|
)
|
|
9345
9705
|
);
|
|
9346
|
-
const rulePath =
|
|
9706
|
+
const rulePath = path24.join(gitRoot ?? cwd, ".cursor", "rules", "anchor.mdc");
|
|
9347
9707
|
checks.push(
|
|
9348
9708
|
check(
|
|
9349
9709
|
"Cursor rule file exists",
|
|
9350
|
-
|
|
9351
|
-
|
|
9710
|
+
fs15.existsSync(rulePath),
|
|
9711
|
+
fs15.existsSync(rulePath) ? "Cursor rule file exists." : "Cursor rule file is missing.",
|
|
9352
9712
|
"Run anchor init to create .cursor/rules/anchor.mdc."
|
|
9353
9713
|
)
|
|
9354
9714
|
);
|
|
@@ -9425,6 +9785,7 @@ export {
|
|
|
9425
9785
|
clampMaxResults,
|
|
9426
9786
|
classifyArchitectureArea,
|
|
9427
9787
|
clearGraphQLFetchCheckpoint,
|
|
9788
|
+
clearOrgHeartbeat,
|
|
9428
9789
|
clipSentence,
|
|
9429
9790
|
cloneOrPullOrgRepo,
|
|
9430
9791
|
cloneOrgRepos,
|
|
@@ -9478,6 +9839,7 @@ export {
|
|
|
9478
9839
|
getLastSyncTime,
|
|
9479
9840
|
getOrgArchitectureMap,
|
|
9480
9841
|
getOrgGraphCounts,
|
|
9842
|
+
getOrgGraphState,
|
|
9481
9843
|
getOrgRepoState,
|
|
9482
9844
|
getOrgStatus,
|
|
9483
9845
|
getPlaybook,
|
|
@@ -9510,10 +9872,13 @@ export {
|
|
|
9510
9872
|
mergeAnchorMcpConfig,
|
|
9511
9873
|
normalizePullRequest,
|
|
9512
9874
|
openAnchorDatabase,
|
|
9875
|
+
openAnchorDatabaseReadOnly,
|
|
9513
9876
|
openOrgDatabase,
|
|
9877
|
+
openOrgDatabaseReadOnly,
|
|
9514
9878
|
orgCloneStateFromResult,
|
|
9515
9879
|
orgConfigPath,
|
|
9516
9880
|
orgDatabasePath,
|
|
9881
|
+
orgHeartbeatPath,
|
|
9517
9882
|
orgRepoLocalPath,
|
|
9518
9883
|
orgReposRoot,
|
|
9519
9884
|
orgRoot,
|
|
@@ -9527,6 +9892,7 @@ export {
|
|
|
9527
9892
|
rankRelevantTests,
|
|
9528
9893
|
rankTeamRules,
|
|
9529
9894
|
rankWisdomUnits,
|
|
9895
|
+
readOrgHeartbeat,
|
|
9530
9896
|
rebuildOrgGraph,
|
|
9531
9897
|
recordFeedback,
|
|
9532
9898
|
recordIndexRun,
|
|
@@ -9570,6 +9936,7 @@ export {
|
|
|
9570
9936
|
validateOrgRepoFullName,
|
|
9571
9937
|
validateOrgRepoGroup,
|
|
9572
9938
|
validateTeamRulesFile,
|
|
9573
|
-
watchCodebase
|
|
9939
|
+
watchCodebase,
|
|
9940
|
+
writeOrgHeartbeat
|
|
9574
9941
|
};
|
|
9575
9942
|
//# sourceMappingURL=index.js.map
|