@codragraph/cli 2.1.6 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/dist/cli/graphpack.d.ts +48 -0
- package/dist/cli/graphpack.js +217 -0
- package/dist/cli/index.js +72 -0
- package/dist/cli/tool.d.ts +1 -0
- package/dist/cli/tool.js +111 -2
- package/dist/core/graphpack/index.d.ts +14 -0
- package/dist/core/graphpack/index.js +474 -0
- package/dist/core/graphpack/types.d.ts +129 -0
- package/dist/core/graphpack/types.js +4 -0
- package/dist/core/semantic/relationships.d.ts +36 -0
- package/dist/core/semantic/relationships.js +261 -0
- package/dist/mcp/local/local-backend.js +42 -0
- package/dist/mcp/resources.js +125 -0
- package/dist/mcp/tools.js +105 -0
- package/dist/server/api.js +112 -0
- package/dist/web/assets/agent-CQNZQ-hg.js +1139 -0
- package/dist/web/assets/{architectureDiagram-UL44E2DR-DFSpa3Hb.js → architectureDiagram-UL44E2DR-B5_goS_i.js} +1 -1
- package/dist/web/assets/{blockDiagram-7IZFK4PR-DlFaxH1b.js → blockDiagram-7IZFK4PR-D7ZAlDyv.js} +1 -1
- package/dist/web/assets/{c4Diagram-Y2BXMSZH-BjJ_Yrim.js → c4Diagram-Y2BXMSZH-Djcgm_54.js} +1 -1
- package/dist/web/assets/{chunk-3SSMPTDK-KGZSzG3Y.js → chunk-3SSMPTDK-Cv2Zy2FO.js} +1 -1
- package/dist/web/assets/{chunk-6764PJDD-p1sGJgVm.js → chunk-6764PJDD-Cppb-jH-.js} +1 -1
- package/dist/web/assets/{chunk-AZZRMDJM-DIDkQA4V.js → chunk-AZZRMDJM-BHlLC7p3.js} +1 -1
- package/dist/web/assets/{chunk-JQRUD6KW-DAwg-yCU.js → chunk-JQRUD6KW-3F8Zg-1N.js} +1 -1
- package/dist/web/assets/{chunk-KRXBNO2N-ChVO_XdS.js → chunk-KRXBNO2N-C0mbN9a7.js} +1 -1
- package/dist/web/assets/{chunk-LCXTWHL2-DGYdb_Eh.js → chunk-LCXTWHL2-BoiuJpIF.js} +1 -1
- package/dist/web/assets/{chunk-LII3EMHJ-Bzh9SNgD.js → chunk-LII3EMHJ-Dqq0Qguw.js} +1 -1
- package/dist/web/assets/{chunk-RG4AUYOV-Bcl7U_IV.js → chunk-RG4AUYOV-Bl5F_gDs.js} +1 -1
- package/dist/web/assets/{chunk-T5OCTHI4-CZYMg5sc.js → chunk-T5OCTHI4-B2tIcggA.js} +1 -1
- package/dist/web/assets/{chunk-W44A43WB-REOI67PN.js → chunk-W44A43WB-BHe37iN7.js} +1 -1
- package/dist/web/assets/{chunk-ZXARS5L4-BfFdV1tf.js → chunk-ZXARS5L4-wcrIaQvY.js} +1 -1
- package/dist/web/assets/classDiagram-KGZ6W3CR-IbI6v_24.js +1 -0
- package/dist/web/assets/classDiagram-v2-72OJOZXJ-IbI6v_24.js +1 -0
- package/dist/web/assets/{cose-bilkent-UX7MHV2Q-D6vANJGG.js → cose-bilkent-UX7MHV2Q-BWr7v0Wr.js} +1 -1
- package/dist/web/assets/{dagre-ND4H6XIP-BiHe5Lal.js → dagre-ND4H6XIP-De5LIh1B.js} +1 -1
- package/dist/web/assets/{diagram-3NCE3AQN-CEutBCOW.js → diagram-3NCE3AQN-Dd22FSHy.js} +1 -1
- package/dist/web/assets/{diagram-GF46GFSD-CZns6HPQ.js → diagram-GF46GFSD-Cev3THY8.js} +1 -1
- package/dist/web/assets/{diagram-HNR7UZ2L-Vz8fE5of.js → diagram-HNR7UZ2L-D8Z8RQGs.js} +1 -1
- package/dist/web/assets/{diagram-QXG6HAR7-D60HKZ_y.js → diagram-QXG6HAR7-B8VOJOiE.js} +1 -1
- package/dist/web/assets/{diagram-WEQXMOUZ-vGAf1p3E.js → diagram-WEQXMOUZ-va1bLoMD.js} +1 -1
- package/dist/web/assets/{erDiagram-L5TCEMPS-DZaplJA6.js → erDiagram-L5TCEMPS-B3_9uAoP.js} +1 -1
- package/dist/web/assets/{flowDiagram-H6V6AXG4-BqUqeAsI.js → flowDiagram-H6V6AXG4-98m6maI1.js} +1 -1
- package/dist/web/assets/{ganttDiagram-JCBTUEKG-XEB6H-0G.js → ganttDiagram-JCBTUEKG-vE2nzETb.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-S2ZK5IYY-7G50u1Cd.js → gitGraphDiagram-S2ZK5IYY-DKc8uUg_.js} +1 -1
- package/dist/web/assets/index-BAhe1HSk.css +1 -0
- package/dist/web/assets/{index-B5WxtMpv.js → index-VTKdaklA.js} +230 -230
- package/dist/web/assets/{infoDiagram-3YFTVSEB-Cut_rzaf.js → infoDiagram-3YFTVSEB-DYP-Srzx.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-BNXS4ZKH-B4DGfGi3.js → ishikawaDiagram-BNXS4ZKH-QZnkpmmb.js} +1 -1
- package/dist/web/assets/{journeyDiagram-M6C3CM3L-BBFhsL3E.js → journeyDiagram-M6C3CM3L-B5ojIuqu.js} +1 -1
- package/dist/web/assets/{kanban-definition-75IXJCU3-DarGRyn3.js → kanban-definition-75IXJCU3-BJA8liRR.js} +1 -1
- package/dist/web/assets/{katex-K3KEBU37-W5XTYMhr.js → katex-K3KEBU37-DUqZiCRL.js} +1 -1
- package/dist/web/assets/{mindmap-definition-2TDM6QVE-BgeczIJM.js → mindmap-definition-2TDM6QVE-BQj5yylD.js} +1 -1
- package/dist/web/assets/{pieDiagram-CU6KROY3-Kkoo-Noq.js → pieDiagram-CU6KROY3-4eSrPiQz.js} +1 -1
- package/dist/web/assets/{quadrantDiagram-VICAPDV7-CDQFeRWN.js → quadrantDiagram-VICAPDV7-PzxN8j55.js} +1 -1
- package/dist/web/assets/{requirementDiagram-JXO7QTGE-Cz9-XnkA.js → requirementDiagram-JXO7QTGE-CtplTc5y.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-URQDO5SZ-CU26z0n7.js → sankeyDiagram-URQDO5SZ-CoSgvkxv.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-VS2MUI6T-OGK1FLOt.js → sequenceDiagram-VS2MUI6T-D7ygyXvJ.js} +1 -1
- package/dist/web/assets/{stateDiagram-7D4R322I-DJ9brq0U.js → stateDiagram-7D4R322I-v01gvwji.js} +1 -1
- package/dist/web/assets/stateDiagram-v2-36443NZ5-DFD2b8_x.js +1 -0
- package/dist/web/assets/{timeline-definition-O6YCAMPW-XZvnjqTT.js → timeline-definition-O6YCAMPW-CTI3M65J.js} +1 -1
- package/dist/web/assets/{vennDiagram-MWXL3ELB-CJUssEjA.js → vennDiagram-MWXL3ELB-RnB0XMP7.js} +1 -1
- package/dist/web/assets/{wardley-L42UT6IY-5TKZOOLJ-DZr11zBG.js → wardley-L42UT6IY-5TKZOOLJ-C-ZcgEBb.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-CUQ6CDDI-C276iqrN.js → wardleyDiagram-CUQ6CDDI-EwRi4kwo.js} +1 -1
- package/dist/web/assets/{xychartDiagram-N2JHSOCM-B9-uCZyP.js → xychartDiagram-N2JHSOCM-DA38II6y.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +2 -2
- package/dist/web/assets/__vite-browser-external-BIHI7g3E.js +0 -1
- package/dist/web/assets/agent-DcdaQnmu.js +0 -1104
- package/dist/web/assets/classDiagram-KGZ6W3CR-B-qkKMYi.js +0 -1
- package/dist/web/assets/classDiagram-v2-72OJOZXJ-B-qkKMYi.js +0 -1
- package/dist/web/assets/index-CT0GtFLZ.css +0 -1
- package/dist/web/assets/stateDiagram-v2-36443NZ5-DhJ4Ky-7.js +0 -1
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { FsCAS, getJson, readCommit, readHead, resolveHeadCommit, } from '@codragraph/graphstore';
|
|
5
|
+
import { GRAPHSTORE_SUBDIR } from '../graphstore/index.js';
|
|
6
|
+
import { getCurrentCommit, getRemoteUrl } from '../../storage/git.js';
|
|
7
|
+
import { INDEX_SCHEMA_VERSION, loadMeta } from '../../storage/repo-manager.js';
|
|
8
|
+
import { GRAPHPACK_LOCK_KIND, GRAPHPACK_LOCK_SCHEMA_VERSION, GRAPHPACK_MANIFEST_SCHEMA_VERSION, INDEX_LOCK_RELATIVE_PATH, } from './types.js';
|
|
9
|
+
export * from './types.js';
|
|
10
|
+
export const defaultLockPath = (repoPath) => path.join(repoPath, INDEX_LOCK_RELATIVE_PATH);
|
|
11
|
+
export const readGraphpackLock = async (lockPath) => {
|
|
12
|
+
try {
|
|
13
|
+
const raw = await fs.readFile(lockPath, 'utf-8');
|
|
14
|
+
const parsed = JSON.parse(raw);
|
|
15
|
+
if (parsed.kind !== GRAPHPACK_LOCK_KIND) {
|
|
16
|
+
throw new Error(`Unexpected graphpack lock kind: ${String(parsed.kind)}`);
|
|
17
|
+
}
|
|
18
|
+
return parsed;
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
if (err.code === 'ENOENT')
|
|
22
|
+
return null;
|
|
23
|
+
throw err;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
export const writeGraphpackLock = async (lockPath, lock) => {
|
|
27
|
+
await fs.mkdir(path.dirname(lockPath), { recursive: true });
|
|
28
|
+
await fs.writeFile(lockPath, `${stableJson(lock)}\n`, 'utf-8');
|
|
29
|
+
};
|
|
30
|
+
export const publishGraphpack = async (opts) => {
|
|
31
|
+
const graphstoreRoot = path.join(opts.storagePath, GRAPHSTORE_SUBDIR);
|
|
32
|
+
const cas = new FsCAS({ root: graphstoreRoot });
|
|
33
|
+
const headCommit = await resolveHeadCommit({ root: graphstoreRoot });
|
|
34
|
+
if (headCommit === null) {
|
|
35
|
+
throw new Error('graphpack publish requires a graphstore HEAD. Run `codragraph analyze` first.');
|
|
36
|
+
}
|
|
37
|
+
const commit = await readCommit(cas, headCommit);
|
|
38
|
+
await getJson(cas, commit.snapshot);
|
|
39
|
+
const meta = await loadMeta(opts.storagePath);
|
|
40
|
+
const files = await collectGraphpackFiles(graphstoreRoot);
|
|
41
|
+
const graphstoreDigest = digestManifestFiles(files);
|
|
42
|
+
const id = makeGraphpackId({
|
|
43
|
+
repoName: opts.repoName,
|
|
44
|
+
target: opts.target,
|
|
45
|
+
headCommit,
|
|
46
|
+
snapshot: commit.snapshot,
|
|
47
|
+
});
|
|
48
|
+
const createdAt = new Date().toISOString();
|
|
49
|
+
const artifactDir = opts.artifactDir ?? path.join(opts.storagePath, 'graphpacks', graphpackDirectoryName(id));
|
|
50
|
+
const manifestPath = path.join(artifactDir, 'manifest.json');
|
|
51
|
+
const repoInfo = {
|
|
52
|
+
name: opts.repoName,
|
|
53
|
+
gitCommit: getCurrentCommit(opts.repoPath) || meta?.lastCommit,
|
|
54
|
+
remoteUrl: meta?.remoteUrl ?? getRemoteUrl(opts.repoPath),
|
|
55
|
+
pathHint: normalizeSlash(path.relative(process.cwd(), opts.repoPath) || '.'),
|
|
56
|
+
};
|
|
57
|
+
const overlay = opts.target === 'pr' || opts.baseSnapshotId || opts.headSnapshotId || opts.pullRequest
|
|
58
|
+
? {
|
|
59
|
+
...(opts.baseSnapshotId ? { baseSnapshotId: opts.baseSnapshotId } : {}),
|
|
60
|
+
...(opts.headSnapshotId ? { headSnapshotId: opts.headSnapshotId } : {}),
|
|
61
|
+
...(opts.pullRequest ? { pullRequest: opts.pullRequest } : {}),
|
|
62
|
+
}
|
|
63
|
+
: undefined;
|
|
64
|
+
const manifest = {
|
|
65
|
+
kind: 'codragraph-graphpack-manifest',
|
|
66
|
+
schemaVersion: GRAPHPACK_MANIFEST_SCHEMA_VERSION,
|
|
67
|
+
id,
|
|
68
|
+
target: opts.target,
|
|
69
|
+
createdAt,
|
|
70
|
+
repo: repoInfo,
|
|
71
|
+
graphstore: {
|
|
72
|
+
branch: await currentGraphstoreBranch(graphstoreRoot),
|
|
73
|
+
headCommit,
|
|
74
|
+
snapshot: commit.snapshot,
|
|
75
|
+
digest: graphstoreDigest,
|
|
76
|
+
fileCount: files.length,
|
|
77
|
+
bytes: sumBytes(files),
|
|
78
|
+
files,
|
|
79
|
+
},
|
|
80
|
+
semanticLayerVersion: 'semantic-extractor-v1',
|
|
81
|
+
...(overlay ? { overlay } : {}),
|
|
82
|
+
};
|
|
83
|
+
await fs.mkdir(artifactDir, { recursive: true });
|
|
84
|
+
await fs.writeFile(manifestPath, `${stableJson(manifest)}\n`, 'utf-8');
|
|
85
|
+
if (meta) {
|
|
86
|
+
await fs.writeFile(path.join(artifactDir, 'meta.json'), `${stableJson(meta)}\n`, 'utf-8');
|
|
87
|
+
}
|
|
88
|
+
const manifestStat = await fs.stat(manifestPath);
|
|
89
|
+
const manifestHash = await sha256File(manifestPath);
|
|
90
|
+
const chunks = [
|
|
91
|
+
{
|
|
92
|
+
path: normalizeSlash(path.relative(opts.storagePath, manifestPath)),
|
|
93
|
+
kind: 'manifest',
|
|
94
|
+
sha256: manifestHash,
|
|
95
|
+
bytes: manifestStat.size,
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
path: GRAPHSTORE_SUBDIR,
|
|
99
|
+
kind: 'graphstore-cas',
|
|
100
|
+
sha256: graphstoreDigest,
|
|
101
|
+
bytes: manifest.graphstore.bytes,
|
|
102
|
+
fileCount: manifest.graphstore.fileCount,
|
|
103
|
+
},
|
|
104
|
+
];
|
|
105
|
+
const semanticRelationshipsPath = path.join(opts.storagePath, 'semantic-relationships.json');
|
|
106
|
+
if (await pathExists(semanticRelationshipsPath)) {
|
|
107
|
+
const semanticStat = await fs.stat(semanticRelationshipsPath);
|
|
108
|
+
chunks.push({
|
|
109
|
+
path: 'semantic-relationships.json',
|
|
110
|
+
kind: 'semantic-relationships',
|
|
111
|
+
sha256: await sha256File(semanticRelationshipsPath),
|
|
112
|
+
bytes: semanticStat.size,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
const lock = {
|
|
116
|
+
kind: GRAPHPACK_LOCK_KIND,
|
|
117
|
+
schemaVersion: GRAPHPACK_LOCK_SCHEMA_VERSION,
|
|
118
|
+
repo: repoInfo,
|
|
119
|
+
graphpack: {
|
|
120
|
+
id,
|
|
121
|
+
target: opts.target,
|
|
122
|
+
createdAt,
|
|
123
|
+
...(opts.artifactUrl ? { artifactUrl: opts.artifactUrl } : {}),
|
|
124
|
+
artifactDir: normalizeSlash(path.relative(opts.repoPath, artifactDir)),
|
|
125
|
+
graphstoreBranch: manifest.graphstore.branch,
|
|
126
|
+
graphstoreHeadCommit: headCommit,
|
|
127
|
+
graphstoreSnapshot: commit.snapshot,
|
|
128
|
+
},
|
|
129
|
+
index: {
|
|
130
|
+
schemaVersion: meta?.schemaVersion ?? INDEX_SCHEMA_VERSION,
|
|
131
|
+
analyzerVersion: opts.analyzerVersion,
|
|
132
|
+
compression: meta?.compress ?? 'none',
|
|
133
|
+
semanticLayerVersion: 'semantic-extractor-v1',
|
|
134
|
+
requiredCapabilities: [
|
|
135
|
+
'graphstore-cas:v1',
|
|
136
|
+
'graphpack-lock:v1',
|
|
137
|
+
'semantic-relationships:v1',
|
|
138
|
+
'recipes-subgraph-signature:v1',
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
chunks,
|
|
142
|
+
...(overlay ? { overlay } : {}),
|
|
143
|
+
};
|
|
144
|
+
const lockPath = opts.lockPath ?? defaultLockPath(opts.repoPath);
|
|
145
|
+
await writeGraphpackLock(lockPath, lock);
|
|
146
|
+
return { lockPath, manifestPath, lock, manifest };
|
|
147
|
+
};
|
|
148
|
+
export const getGraphpackStatus = async (opts) => {
|
|
149
|
+
const lockPath = opts.lockPath ?? defaultLockPath(opts.repoPath);
|
|
150
|
+
const lock = await readGraphpackLock(lockPath);
|
|
151
|
+
const graphstoreRoot = path.join(opts.storagePath, GRAPHSTORE_SUBDIR);
|
|
152
|
+
const localHead = await safeResolveHeadCommit(graphstoreRoot);
|
|
153
|
+
const meta = await loadMeta(opts.storagePath);
|
|
154
|
+
const compatibilityReasons = [];
|
|
155
|
+
const missing = [];
|
|
156
|
+
const mismatched = [];
|
|
157
|
+
let verified = 0;
|
|
158
|
+
if (lock) {
|
|
159
|
+
if (lock.schemaVersion !== GRAPHPACK_LOCK_SCHEMA_VERSION) {
|
|
160
|
+
compatibilityReasons.push(`lock schema ${lock.schemaVersion} is not supported by this CLI (${GRAPHPACK_LOCK_SCHEMA_VERSION})`);
|
|
161
|
+
}
|
|
162
|
+
if (lock.index.schemaVersion !== undefined && lock.index.schemaVersion > INDEX_SCHEMA_VERSION) {
|
|
163
|
+
compatibilityReasons.push(`index schema ${lock.index.schemaVersion} requires a newer analyzer than this CLI (${INDEX_SCHEMA_VERSION})`);
|
|
164
|
+
}
|
|
165
|
+
for (const chunk of lock.chunks) {
|
|
166
|
+
if (chunk.kind === 'graphstore-cas') {
|
|
167
|
+
if (!(await pathExists(graphstoreRoot))) {
|
|
168
|
+
missing.push(chunk.path);
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (opts.strict) {
|
|
172
|
+
const files = await collectGraphpackFiles(graphstoreRoot);
|
|
173
|
+
const digest = digestManifestFiles(files);
|
|
174
|
+
if (digest === chunk.sha256)
|
|
175
|
+
verified++;
|
|
176
|
+
else
|
|
177
|
+
mismatched.push(chunk.path);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
verified++;
|
|
181
|
+
}
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
const full = path.join(opts.storagePath, chunk.path);
|
|
185
|
+
if (!(await pathExists(full))) {
|
|
186
|
+
missing.push(chunk.path);
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
const actual = await sha256File(full);
|
|
190
|
+
if (actual === chunk.sha256)
|
|
191
|
+
verified++;
|
|
192
|
+
else
|
|
193
|
+
mismatched.push(chunk.path);
|
|
194
|
+
}
|
|
195
|
+
if (lock.graphpack.graphstoreHeadCommit && localHead) {
|
|
196
|
+
if (lock.graphpack.graphstoreHeadCommit !== localHead) {
|
|
197
|
+
compatibilityReasons.push(`local graphstore HEAD ${shortId(localHead)} does not match lock ${shortId(lock.graphpack.graphstoreHeadCommit)}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const source = !lock
|
|
202
|
+
? localHead
|
|
203
|
+
? 'local'
|
|
204
|
+
: 'missing'
|
|
205
|
+
: lock.graphpack.target === 'pr'
|
|
206
|
+
? 'pr-overlay'
|
|
207
|
+
: 'canonical';
|
|
208
|
+
return {
|
|
209
|
+
repoPath: opts.repoPath,
|
|
210
|
+
storagePath: opts.storagePath,
|
|
211
|
+
lockPath,
|
|
212
|
+
lockPresent: lock !== null,
|
|
213
|
+
...(lock ? { lock } : {}),
|
|
214
|
+
local: {
|
|
215
|
+
graphstorePresent: await pathExists(graphstoreRoot),
|
|
216
|
+
...(localHead ? { headCommit: localHead } : {}),
|
|
217
|
+
...(meta?.schemaVersion !== undefined ? { schemaVersion: meta.schemaVersion } : {}),
|
|
218
|
+
...(meta ? { analyzerVersion: lock?.index.analyzerVersion } : {}),
|
|
219
|
+
},
|
|
220
|
+
compatibility: {
|
|
221
|
+
ok: compatibilityReasons.length === 0 && mismatched.length === 0,
|
|
222
|
+
reasons: compatibilityReasons,
|
|
223
|
+
},
|
|
224
|
+
chunks: {
|
|
225
|
+
expected: lock?.chunks.length ?? 0,
|
|
226
|
+
verified,
|
|
227
|
+
missing,
|
|
228
|
+
mismatched,
|
|
229
|
+
},
|
|
230
|
+
source,
|
|
231
|
+
};
|
|
232
|
+
};
|
|
233
|
+
export const pullGraphpack = async (opts) => {
|
|
234
|
+
const statusBefore = await getGraphpackStatus({
|
|
235
|
+
repoPath: opts.repoPath,
|
|
236
|
+
storagePath: opts.storagePath,
|
|
237
|
+
lockPath: opts.lockPath,
|
|
238
|
+
});
|
|
239
|
+
if (!statusBefore.lock) {
|
|
240
|
+
return {
|
|
241
|
+
status: statusBefore,
|
|
242
|
+
pulled: false,
|
|
243
|
+
materializable: false,
|
|
244
|
+
fallbackRequired: true,
|
|
245
|
+
reason: 'No .codragraph/index.lock.json was found.',
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
const lock = statusBefore.lock;
|
|
249
|
+
const artifactRoots = artifactRootCandidates({
|
|
250
|
+
repoPath: opts.repoPath,
|
|
251
|
+
storagePath: opts.storagePath,
|
|
252
|
+
lock,
|
|
253
|
+
artifactDir: opts.artifactDir,
|
|
254
|
+
});
|
|
255
|
+
const copied = new Set();
|
|
256
|
+
for (const chunk of lock.chunks) {
|
|
257
|
+
const source = await findChunkSource(chunk, artifactRoots);
|
|
258
|
+
if (!source)
|
|
259
|
+
continue;
|
|
260
|
+
if (chunk.kind === 'graphstore-cas') {
|
|
261
|
+
const destination = path.join(opts.storagePath, GRAPHSTORE_SUBDIR);
|
|
262
|
+
if (!samePath(source, destination)) {
|
|
263
|
+
await replaceDirectory(source, destination);
|
|
264
|
+
copied.add(chunk.path);
|
|
265
|
+
}
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
const destination = path.join(opts.storagePath, chunk.path);
|
|
269
|
+
if (samePath(source, destination))
|
|
270
|
+
continue;
|
|
271
|
+
await fs.mkdir(path.dirname(destination), { recursive: true });
|
|
272
|
+
await fs.copyFile(source, destination);
|
|
273
|
+
copied.add(chunk.path);
|
|
274
|
+
}
|
|
275
|
+
const metaSource = await findArtifactFile('meta.json', artifactRoots);
|
|
276
|
+
if (metaSource && !samePath(metaSource, path.join(opts.storagePath, 'meta.json'))) {
|
|
277
|
+
await fs.mkdir(opts.storagePath, { recursive: true });
|
|
278
|
+
await fs.copyFile(metaSource, path.join(opts.storagePath, 'meta.json'));
|
|
279
|
+
copied.add('meta.json');
|
|
280
|
+
}
|
|
281
|
+
const status = await getGraphpackStatus({
|
|
282
|
+
repoPath: opts.repoPath,
|
|
283
|
+
storagePath: opts.storagePath,
|
|
284
|
+
lockPath: opts.lockPath,
|
|
285
|
+
strict: true,
|
|
286
|
+
});
|
|
287
|
+
const fallbackRequired = status.chunks.missing.length > 0 ||
|
|
288
|
+
status.chunks.mismatched.length > 0 ||
|
|
289
|
+
!status.compatibility.ok;
|
|
290
|
+
return {
|
|
291
|
+
status,
|
|
292
|
+
pulled: copied.size > 0,
|
|
293
|
+
materializable: !fallbackRequired &&
|
|
294
|
+
status.lock?.graphpack.graphstoreHeadCommit !== undefined &&
|
|
295
|
+
status.local.graphstorePresent,
|
|
296
|
+
fallbackRequired,
|
|
297
|
+
...(fallbackRequired
|
|
298
|
+
? {
|
|
299
|
+
reason: status.compatibility.reasons[0] ??
|
|
300
|
+
(status.chunks.missing.length > 0
|
|
301
|
+
? `Missing graphpack chunk: ${status.chunks.missing[0]}`
|
|
302
|
+
: status.chunks.mismatched.length > 0
|
|
303
|
+
? `Checksum mismatch for graphpack chunk: ${status.chunks.mismatched[0]}`
|
|
304
|
+
: `No graphpack chunks were found in: ${artifactRoots.join(', ')}`),
|
|
305
|
+
}
|
|
306
|
+
: {}),
|
|
307
|
+
};
|
|
308
|
+
};
|
|
309
|
+
export const bootstrapGraphpack = async (opts) => {
|
|
310
|
+
const pulled = await pullGraphpack(opts);
|
|
311
|
+
return {
|
|
312
|
+
...pulled,
|
|
313
|
+
fallbackCommand: 'codragraph analyze --skip-agents-md --no-setup',
|
|
314
|
+
};
|
|
315
|
+
};
|
|
316
|
+
const collectGraphpackFiles = async (root) => {
|
|
317
|
+
const files = [];
|
|
318
|
+
await walkFiles(root, async (filePath) => {
|
|
319
|
+
const rel = normalizeSlash(path.relative(path.dirname(root), filePath));
|
|
320
|
+
const stat = await fs.stat(filePath);
|
|
321
|
+
files.push({ path: rel, sha256: await sha256File(filePath), bytes: stat.size });
|
|
322
|
+
});
|
|
323
|
+
return files.sort((a, b) => a.path.localeCompare(b.path));
|
|
324
|
+
};
|
|
325
|
+
const walkFiles = async (root, visit) => {
|
|
326
|
+
let entries;
|
|
327
|
+
try {
|
|
328
|
+
entries = await fs.readdir(root, { withFileTypes: true });
|
|
329
|
+
}
|
|
330
|
+
catch (err) {
|
|
331
|
+
if (err.code === 'ENOENT')
|
|
332
|
+
return;
|
|
333
|
+
throw err;
|
|
334
|
+
}
|
|
335
|
+
for (const entry of entries) {
|
|
336
|
+
const full = path.join(root, entry.name);
|
|
337
|
+
if (entry.isDirectory())
|
|
338
|
+
await walkFiles(full, visit);
|
|
339
|
+
else if (entry.isFile())
|
|
340
|
+
await visit(full);
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
const sha256File = async (filePath) => {
|
|
344
|
+
const hash = crypto.createHash('sha256');
|
|
345
|
+
hash.update(await fs.readFile(filePath));
|
|
346
|
+
return hash.digest('hex');
|
|
347
|
+
};
|
|
348
|
+
const digestManifestFiles = (files) => {
|
|
349
|
+
const canonical = stableJson(files.map((f) => ({ path: f.path, sha256: f.sha256, bytes: f.bytes })));
|
|
350
|
+
return crypto.createHash('sha256').update(canonical).digest('hex');
|
|
351
|
+
};
|
|
352
|
+
const stableJson = (value) => JSON.stringify(value, (_key, v) => {
|
|
353
|
+
if (v && typeof v === 'object' && !Array.isArray(v)) {
|
|
354
|
+
const sorted = {};
|
|
355
|
+
for (const k of Object.keys(v).sort()) {
|
|
356
|
+
sorted[k] = v[k];
|
|
357
|
+
}
|
|
358
|
+
return sorted;
|
|
359
|
+
}
|
|
360
|
+
return v;
|
|
361
|
+
}, 2);
|
|
362
|
+
const sumBytes = (files) => files.reduce((sum, f) => sum + f.bytes, 0);
|
|
363
|
+
const makeGraphpackId = (input) => {
|
|
364
|
+
const digest = crypto
|
|
365
|
+
.createHash('sha256')
|
|
366
|
+
.update(`${input.repoName}\n${input.target}\n${input.headCommit}\n${input.snapshot}`)
|
|
367
|
+
.digest('hex');
|
|
368
|
+
return `gpk_${input.target}_${digest.slice(0, 20)}`;
|
|
369
|
+
};
|
|
370
|
+
const graphpackDirectoryName = (id) => id.replace(/[^A-Za-z0-9._-]/g, '_');
|
|
371
|
+
const currentGraphstoreBranch = async (root) => {
|
|
372
|
+
try {
|
|
373
|
+
const head = await readHead({ root });
|
|
374
|
+
return head.kind === 'branch' ? head.branch : undefined;
|
|
375
|
+
}
|
|
376
|
+
catch {
|
|
377
|
+
return undefined;
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
const safeResolveHeadCommit = async (root) => {
|
|
381
|
+
try {
|
|
382
|
+
return (await resolveHeadCommit({ root })) ?? undefined;
|
|
383
|
+
}
|
|
384
|
+
catch {
|
|
385
|
+
return undefined;
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
const pathExists = async (target) => {
|
|
389
|
+
try {
|
|
390
|
+
await fs.access(target);
|
|
391
|
+
return true;
|
|
392
|
+
}
|
|
393
|
+
catch {
|
|
394
|
+
return false;
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
const resolveArtifactDir = (repoPath, lock) => {
|
|
398
|
+
if (lock.graphpack.artifactDir) {
|
|
399
|
+
return path.resolve(repoPath, lock.graphpack.artifactDir);
|
|
400
|
+
}
|
|
401
|
+
if (lock.graphpack.artifactUrl?.startsWith('file://')) {
|
|
402
|
+
return new URL(lock.graphpack.artifactUrl).pathname;
|
|
403
|
+
}
|
|
404
|
+
return undefined;
|
|
405
|
+
};
|
|
406
|
+
const artifactRootCandidates = (input) => {
|
|
407
|
+
const candidates = [];
|
|
408
|
+
const addRootAndParents = (value) => {
|
|
409
|
+
if (!value)
|
|
410
|
+
return;
|
|
411
|
+
const resolved = path.resolve(value);
|
|
412
|
+
candidates.push(resolved);
|
|
413
|
+
candidates.push(path.join(resolved, '.codragraph'));
|
|
414
|
+
let cursor = resolved;
|
|
415
|
+
for (let i = 0; i < 3; i++) {
|
|
416
|
+
const parent = path.dirname(cursor);
|
|
417
|
+
if (parent === cursor)
|
|
418
|
+
break;
|
|
419
|
+
candidates.push(parent);
|
|
420
|
+
cursor = parent;
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
addRootAndParents(input.artifactDir);
|
|
424
|
+
addRootAndParents(resolveArtifactDir(input.repoPath, input.lock));
|
|
425
|
+
candidates.push(input.storagePath);
|
|
426
|
+
return [...new Set(candidates.map((candidate) => path.resolve(candidate)))];
|
|
427
|
+
};
|
|
428
|
+
const findChunkSource = async (chunk, roots) => {
|
|
429
|
+
for (const root of roots) {
|
|
430
|
+
const candidates = chunk.kind === 'graphstore-cas'
|
|
431
|
+
? [
|
|
432
|
+
path.join(root, chunk.path),
|
|
433
|
+
path.join(root, GRAPHSTORE_SUBDIR),
|
|
434
|
+
path.basename(root) === GRAPHSTORE_SUBDIR ? root : undefined,
|
|
435
|
+
]
|
|
436
|
+
: [
|
|
437
|
+
path.join(root, chunk.path),
|
|
438
|
+
path.join(root, path.basename(chunk.path)),
|
|
439
|
+
chunk.kind === 'manifest' ? path.join(root, 'manifest.json') : undefined,
|
|
440
|
+
];
|
|
441
|
+
for (const candidate of candidates) {
|
|
442
|
+
if (candidate && (await pathExists(candidate)))
|
|
443
|
+
return candidate;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
return undefined;
|
|
447
|
+
};
|
|
448
|
+
const findArtifactFile = async (relativePath, roots) => {
|
|
449
|
+
for (const root of roots) {
|
|
450
|
+
const candidate = path.join(root, relativePath);
|
|
451
|
+
if (await pathExists(candidate))
|
|
452
|
+
return candidate;
|
|
453
|
+
}
|
|
454
|
+
return undefined;
|
|
455
|
+
};
|
|
456
|
+
const replaceDirectory = async (from, to) => {
|
|
457
|
+
await fs.rm(to, { recursive: true, force: true });
|
|
458
|
+
await copyDirectory(from, to);
|
|
459
|
+
};
|
|
460
|
+
const copyDirectory = async (from, to) => {
|
|
461
|
+
await fs.mkdir(to, { recursive: true });
|
|
462
|
+
const entries = await fs.readdir(from, { withFileTypes: true });
|
|
463
|
+
for (const entry of entries) {
|
|
464
|
+
const src = path.join(from, entry.name);
|
|
465
|
+
const dst = path.join(to, entry.name);
|
|
466
|
+
if (entry.isDirectory())
|
|
467
|
+
await copyDirectory(src, dst);
|
|
468
|
+
else if (entry.isFile())
|
|
469
|
+
await fs.copyFile(src, dst);
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
const samePath = (a, b) => path.resolve(a) === path.resolve(b);
|
|
473
|
+
const normalizeSlash = (value) => value.replace(/\\/g, '/');
|
|
474
|
+
const shortId = (id) => id.startsWith('sha256:') ? id.slice(7, 19) : id.slice(0, 12);
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
export declare const GRAPHPACK_LOCK_KIND: "codragraph-index-lock";
|
|
2
|
+
export declare const GRAPHPACK_LOCK_SCHEMA_VERSION: 1;
|
|
3
|
+
export declare const INDEX_LOCK_RELATIVE_PATH: ".codragraph/index.lock.json";
|
|
4
|
+
export declare const GRAPHPACK_MANIFEST_SCHEMA_VERSION: 1;
|
|
5
|
+
export type GraphpackTarget = 'main' | 'pr';
|
|
6
|
+
export type GraphpackChunkKind = 'manifest' | 'graphstore-cas' | 'semantic-relationships' | 'recipe-metadata';
|
|
7
|
+
export interface GraphpackChunk {
|
|
8
|
+
readonly path: string;
|
|
9
|
+
readonly kind: GraphpackChunkKind;
|
|
10
|
+
readonly sha256: string;
|
|
11
|
+
readonly bytes: number;
|
|
12
|
+
readonly fileCount?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface GraphpackLock {
|
|
15
|
+
readonly kind: typeof GRAPHPACK_LOCK_KIND;
|
|
16
|
+
readonly schemaVersion: typeof GRAPHPACK_LOCK_SCHEMA_VERSION;
|
|
17
|
+
readonly repo: {
|
|
18
|
+
readonly name: string;
|
|
19
|
+
readonly gitCommit?: string;
|
|
20
|
+
readonly remoteUrl?: string;
|
|
21
|
+
readonly pathHint?: string;
|
|
22
|
+
};
|
|
23
|
+
readonly graphpack: {
|
|
24
|
+
readonly id: string;
|
|
25
|
+
readonly target: GraphpackTarget;
|
|
26
|
+
readonly createdAt: string;
|
|
27
|
+
readonly artifactUrl?: string;
|
|
28
|
+
readonly artifactDir?: string;
|
|
29
|
+
readonly graphstoreBranch?: string;
|
|
30
|
+
readonly graphstoreHeadCommit?: string;
|
|
31
|
+
readonly graphstoreSnapshot?: string;
|
|
32
|
+
};
|
|
33
|
+
readonly index: {
|
|
34
|
+
readonly schemaVersion?: number;
|
|
35
|
+
readonly analyzerVersion: string;
|
|
36
|
+
readonly compression?: 'none' | 'brotli' | 'zstd';
|
|
37
|
+
readonly semanticLayerVersion: string;
|
|
38
|
+
readonly requiredCapabilities: readonly string[];
|
|
39
|
+
};
|
|
40
|
+
readonly chunks: readonly GraphpackChunk[];
|
|
41
|
+
readonly overlay?: {
|
|
42
|
+
readonly baseSnapshotId?: string;
|
|
43
|
+
readonly headSnapshotId?: string;
|
|
44
|
+
readonly pullRequest?: string;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export interface GraphpackManifest {
|
|
48
|
+
readonly kind: 'codragraph-graphpack-manifest';
|
|
49
|
+
readonly schemaVersion: typeof GRAPHPACK_MANIFEST_SCHEMA_VERSION;
|
|
50
|
+
readonly id: string;
|
|
51
|
+
readonly target: GraphpackTarget;
|
|
52
|
+
readonly createdAt: string;
|
|
53
|
+
readonly repo: GraphpackLock['repo'];
|
|
54
|
+
readonly graphstore: {
|
|
55
|
+
readonly branch?: string;
|
|
56
|
+
readonly headCommit?: string;
|
|
57
|
+
readonly snapshot?: string;
|
|
58
|
+
readonly digest: string;
|
|
59
|
+
readonly fileCount: number;
|
|
60
|
+
readonly bytes: number;
|
|
61
|
+
readonly files: readonly GraphpackManifestFile[];
|
|
62
|
+
};
|
|
63
|
+
readonly semanticLayerVersion: string;
|
|
64
|
+
readonly overlay?: GraphpackLock['overlay'];
|
|
65
|
+
}
|
|
66
|
+
export interface GraphpackManifestFile {
|
|
67
|
+
readonly path: string;
|
|
68
|
+
readonly sha256: string;
|
|
69
|
+
readonly bytes: number;
|
|
70
|
+
}
|
|
71
|
+
export interface GraphpackStatus {
|
|
72
|
+
readonly repoPath: string;
|
|
73
|
+
readonly storagePath: string;
|
|
74
|
+
readonly lockPath: string;
|
|
75
|
+
readonly lockPresent: boolean;
|
|
76
|
+
readonly lock?: GraphpackLock;
|
|
77
|
+
readonly local: {
|
|
78
|
+
readonly graphstorePresent: boolean;
|
|
79
|
+
readonly headCommit?: string;
|
|
80
|
+
readonly schemaVersion?: number;
|
|
81
|
+
readonly analyzerVersion?: string;
|
|
82
|
+
};
|
|
83
|
+
readonly compatibility: {
|
|
84
|
+
readonly ok: boolean;
|
|
85
|
+
readonly reasons: string[];
|
|
86
|
+
};
|
|
87
|
+
readonly chunks: {
|
|
88
|
+
readonly expected: number;
|
|
89
|
+
readonly verified: number;
|
|
90
|
+
readonly missing: readonly string[];
|
|
91
|
+
readonly mismatched: readonly string[];
|
|
92
|
+
};
|
|
93
|
+
readonly source: 'canonical' | 'pr-overlay' | 'local' | 'missing';
|
|
94
|
+
}
|
|
95
|
+
export interface GraphpackPublishOptions {
|
|
96
|
+
readonly repoPath: string;
|
|
97
|
+
readonly storagePath: string;
|
|
98
|
+
readonly repoName: string;
|
|
99
|
+
readonly analyzerVersion: string;
|
|
100
|
+
readonly target: GraphpackTarget;
|
|
101
|
+
readonly artifactDir?: string;
|
|
102
|
+
readonly artifactUrl?: string;
|
|
103
|
+
readonly baseSnapshotId?: string;
|
|
104
|
+
readonly headSnapshotId?: string;
|
|
105
|
+
readonly pullRequest?: string;
|
|
106
|
+
readonly lockPath?: string;
|
|
107
|
+
}
|
|
108
|
+
export interface GraphpackPublishResult {
|
|
109
|
+
readonly lockPath: string;
|
|
110
|
+
readonly manifestPath: string;
|
|
111
|
+
readonly lock: GraphpackLock;
|
|
112
|
+
readonly manifest: GraphpackManifest;
|
|
113
|
+
}
|
|
114
|
+
export interface GraphpackPullOptions {
|
|
115
|
+
readonly repoPath: string;
|
|
116
|
+
readonly storagePath: string;
|
|
117
|
+
readonly lockPath?: string;
|
|
118
|
+
readonly artifactDir?: string;
|
|
119
|
+
}
|
|
120
|
+
export interface GraphpackPullResult {
|
|
121
|
+
readonly status: GraphpackStatus;
|
|
122
|
+
readonly pulled: boolean;
|
|
123
|
+
readonly materializable: boolean;
|
|
124
|
+
readonly fallbackRequired: boolean;
|
|
125
|
+
readonly reason?: string;
|
|
126
|
+
}
|
|
127
|
+
export interface GraphpackBootstrapResult extends GraphpackPullResult {
|
|
128
|
+
readonly fallbackCommand: string;
|
|
129
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export declare const SEMANTIC_RELATIONSHIP_VERSION: "semantic-extractor-v1";
|
|
2
|
+
export type SemanticRelationshipFamily = 'COMPOSES' | 'ADAPTS' | 'DELEGATES_TO' | 'WRAPS' | 'CONFIGURES' | 'FACTORY_CREATES' | 'ORCHESTRATES' | 'PROXIES_TO' | 'MAPS_TO';
|
|
3
|
+
export type SemanticRelationshipProvenance = 'extracted' | 'inferred' | 'LLM_INFERRED' | 'human-confirmed';
|
|
4
|
+
export interface SemanticRelationshipEvidence {
|
|
5
|
+
readonly filePath?: string;
|
|
6
|
+
readonly startLine?: number;
|
|
7
|
+
readonly endLine?: number;
|
|
8
|
+
readonly rawEdgeType: string;
|
|
9
|
+
readonly rawEdgeConfidence?: number;
|
|
10
|
+
readonly reason: string;
|
|
11
|
+
}
|
|
12
|
+
export interface SemanticRelationship {
|
|
13
|
+
readonly id: string;
|
|
14
|
+
readonly family: SemanticRelationshipFamily;
|
|
15
|
+
readonly sourceId: string;
|
|
16
|
+
readonly sourceName?: string;
|
|
17
|
+
readonly targetId: string;
|
|
18
|
+
readonly targetName?: string;
|
|
19
|
+
readonly confidence: number;
|
|
20
|
+
readonly provenance: SemanticRelationshipProvenance;
|
|
21
|
+
readonly extractorVersion: typeof SEMANTIC_RELATIONSHIP_VERSION;
|
|
22
|
+
readonly evidence: SemanticRelationshipEvidence;
|
|
23
|
+
}
|
|
24
|
+
export interface SemanticRelationshipReport {
|
|
25
|
+
readonly snapshotId?: string;
|
|
26
|
+
readonly extractorVersion: typeof SEMANTIC_RELATIONSHIP_VERSION;
|
|
27
|
+
readonly llmEnabled: boolean;
|
|
28
|
+
readonly relationships: readonly SemanticRelationship[];
|
|
29
|
+
readonly summary: Record<SemanticRelationshipFamily, number>;
|
|
30
|
+
}
|
|
31
|
+
export declare const analyzeSemanticRelationships: (opts: {
|
|
32
|
+
readonly storagePath: string;
|
|
33
|
+
readonly limit?: number;
|
|
34
|
+
readonly llm?: boolean;
|
|
35
|
+
readonly write?: boolean;
|
|
36
|
+
}) => Promise<SemanticRelationshipReport>;
|