maskweaver 0.8.3 → 0.8.4

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.
@@ -0,0 +1,59 @@
1
+ export type GdcEnabledMode = boolean | 'auto';
2
+ export interface EffectiveGdcConfig {
3
+ enabled: boolean;
4
+ mode: GdcEnabledMode;
5
+ detected: boolean;
6
+ strictVerify: boolean;
7
+ binPath: string;
8
+ autoSyncOnPrepare: boolean;
9
+ }
10
+ export interface GdcMachineEnvelope {
11
+ ok?: boolean;
12
+ contractVersion?: string;
13
+ command?: string;
14
+ timestamp?: string;
15
+ data?: unknown;
16
+ warnings?: unknown[];
17
+ errors?: unknown[];
18
+ meta?: Record<string, unknown>;
19
+ }
20
+ export interface GdcMachineCommandResult {
21
+ command: string;
22
+ args: string[];
23
+ exitCode: number;
24
+ stdout: string;
25
+ stderr: string;
26
+ durationMs: number;
27
+ timedOut: boolean;
28
+ transportError?: string;
29
+ parseError?: string;
30
+ envelope?: GdcMachineEnvelope;
31
+ data?: unknown;
32
+ }
33
+ export declare function detectGdcWorkspace(basePath: string): boolean;
34
+ export declare function getEffectiveGdcConfig(basePath: string): EffectiveGdcConfig;
35
+ export declare function runGdcMachineCommand(options: {
36
+ basePath: string;
37
+ command: string;
38
+ args?: string[];
39
+ timeoutMs?: number;
40
+ maxOutputChars?: number;
41
+ config?: EffectiveGdcConfig;
42
+ }): Promise<GdcMachineCommandResult>;
43
+ export declare function countGdcCheckIssues(data: unknown): {
44
+ errors: number;
45
+ warnings: number;
46
+ infos: number;
47
+ issueCount: number;
48
+ };
49
+ export declare function getGraphNodeIds(data: unknown): string[];
50
+ export declare function getGraphEdges(data: unknown): Array<{
51
+ from: string;
52
+ to: string;
53
+ }>;
54
+ export declare function getStatsNodeSummary(data: unknown): {
55
+ total?: number;
56
+ implemented?: number;
57
+ tested?: number;
58
+ };
59
+ //# sourceMappingURL=gdc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gdc.d.ts","sourceRoot":"","sources":["../../src/weave/gdc.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,MAAM,CAAC;AAE9C,MAAM,WAAW,kBAAkB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,cAAc,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,kBAAkB;IAC/B,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,uBAAuB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,IAAI,CAAC,EAAE,OAAO,CAAC;CAClB;AAgCD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAO5D;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,CAc1E;AA8CD,wBAAsB,oBAAoB,CAAC,OAAO,EAAE;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,kBAAkB,CAAC;CAC/B,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAsEnC;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACtB,CA0BA;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,EAAE,CAYvD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,CAahF;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB,CASA"}
@@ -0,0 +1,221 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { execFile } from 'node:child_process';
4
+ import { promisify } from 'node:util';
5
+ import { getGdcConfig } from '../shared/config.js';
6
+ const execFileAsync = promisify(execFile);
7
+ function asObject(value) {
8
+ if (!value || typeof value !== 'object' || Array.isArray(value))
9
+ return null;
10
+ return value;
11
+ }
12
+ function asArray(value) {
13
+ return Array.isArray(value) ? value : [];
14
+ }
15
+ function asNumber(value) {
16
+ if (typeof value === 'number' && Number.isFinite(value))
17
+ return value;
18
+ if (typeof value === 'string' && value.trim().length > 0) {
19
+ const parsed = Number(value);
20
+ if (Number.isFinite(parsed))
21
+ return parsed;
22
+ }
23
+ return undefined;
24
+ }
25
+ function isMachineEnvelope(value) {
26
+ const obj = asObject(value);
27
+ if (!obj)
28
+ return false;
29
+ return (Object.prototype.hasOwnProperty.call(obj, 'data')
30
+ || Object.prototype.hasOwnProperty.call(obj, 'ok')
31
+ || Object.prototype.hasOwnProperty.call(obj, 'contractVersion')
32
+ || Object.prototype.hasOwnProperty.call(obj, 'warnings')
33
+ || Object.prototype.hasOwnProperty.call(obj, 'errors'));
34
+ }
35
+ export function detectGdcWorkspace(basePath) {
36
+ const gdcDir = path.join(basePath, '.gdc');
37
+ return (fs.existsSync(gdcDir)
38
+ || fs.existsSync(path.join(gdcDir, 'config.yaml'))
39
+ || fs.existsSync(path.join(gdcDir, 'nodes')));
40
+ }
41
+ export function getEffectiveGdcConfig(basePath) {
42
+ const config = getGdcConfig(basePath);
43
+ const mode = (config?.enabled ?? 'auto');
44
+ const detected = detectGdcWorkspace(basePath);
45
+ const enabled = mode === true || (mode !== false && detected);
46
+ return {
47
+ enabled,
48
+ mode,
49
+ detected,
50
+ strictVerify: config?.strictVerify ?? false,
51
+ binPath: config?.binPath?.trim() || 'gdc',
52
+ autoSyncOnPrepare: config?.autoSyncOnPrepare ?? true,
53
+ };
54
+ }
55
+ function parseMachineOutput(stdout) {
56
+ const text = stdout.trim();
57
+ if (!text) {
58
+ return { parseError: 'empty stdout' };
59
+ }
60
+ try {
61
+ const parsed = JSON.parse(text);
62
+ if (isMachineEnvelope(parsed)) {
63
+ const envelope = parsed;
64
+ return { envelope, data: envelope.data };
65
+ }
66
+ return { data: parsed };
67
+ }
68
+ catch (error) {
69
+ const message = error instanceof Error ? error.message : String(error);
70
+ return { parseError: message };
71
+ }
72
+ }
73
+ function splitCommand(commandText) {
74
+ const trimmed = commandText.trim();
75
+ if (!trimmed)
76
+ return [];
77
+ const tokens = trimmed.match(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|[^\s]+/g) || [];
78
+ return tokens
79
+ .map(token => token.trim())
80
+ .filter(Boolean)
81
+ .map(token => {
82
+ if ((token.startsWith('"') && token.endsWith('"')) || (token.startsWith("'") && token.endsWith("'"))) {
83
+ return token.slice(1, -1);
84
+ }
85
+ return token;
86
+ });
87
+ }
88
+ function toTail(text, maxChars) {
89
+ if (text.length <= maxChars)
90
+ return text;
91
+ return text.slice(text.length - maxChars);
92
+ }
93
+ export async function runGdcMachineCommand(options) {
94
+ const cfg = options.config || getEffectiveGdcConfig(options.basePath);
95
+ const timeoutMs = options.timeoutMs ?? 90_000;
96
+ const maxOutputChars = options.maxOutputChars ?? 200_000;
97
+ const requestedArgs = options.args ? [...options.args] : [];
98
+ if (!requestedArgs.includes('--machine')) {
99
+ requestedArgs.push('--machine');
100
+ }
101
+ const commandParts = splitCommand(cfg.binPath);
102
+ const binary = commandParts[0] || 'gdc';
103
+ const binaryArgs = commandParts.slice(1);
104
+ const fullArgs = [...binaryArgs, options.command, ...requestedArgs];
105
+ const start = Date.now();
106
+ try {
107
+ const { stdout, stderr } = await execFileAsync(binary, fullArgs, {
108
+ cwd: options.basePath,
109
+ timeout: timeoutMs,
110
+ maxBuffer: 2 * 1024 * 1024,
111
+ windowsHide: true,
112
+ });
113
+ const stdoutText = toTail(String(stdout || '').trim(), maxOutputChars);
114
+ const stderrText = toTail(String(stderr || '').trim(), maxOutputChars);
115
+ const parsed = parseMachineOutput(stdoutText);
116
+ return {
117
+ command: options.command,
118
+ args: requestedArgs,
119
+ exitCode: 0,
120
+ stdout: stdoutText,
121
+ stderr: stderrText,
122
+ durationMs: Date.now() - start,
123
+ timedOut: false,
124
+ envelope: parsed.envelope,
125
+ data: parsed.data,
126
+ parseError: parsed.parseError,
127
+ };
128
+ }
129
+ catch (error) {
130
+ const rawCode = error?.code;
131
+ const exitCode = typeof rawCode === 'number'
132
+ ? rawCode
133
+ : rawCode === 'ENOENT'
134
+ ? 127
135
+ : 1;
136
+ const stdoutText = toTail(String(error?.stdout || '').trim(), maxOutputChars);
137
+ const stderrText = toTail(String(error?.stderr || '').trim(), maxOutputChars);
138
+ const timedOut = Boolean(error?.killed)
139
+ || /timed?\s*out/i.test(String(error?.message || ''));
140
+ const parsed = parseMachineOutput(stdoutText);
141
+ const transportError = rawCode === 'ENOENT'
142
+ ? `GDC binary not found: ${cfg.binPath}`
143
+ : (error instanceof Error ? error.message : String(error));
144
+ return {
145
+ command: options.command,
146
+ args: requestedArgs,
147
+ exitCode,
148
+ stdout: stdoutText,
149
+ stderr: stderrText,
150
+ durationMs: Date.now() - start,
151
+ timedOut,
152
+ transportError,
153
+ envelope: parsed.envelope,
154
+ data: parsed.data,
155
+ parseError: parsed.parseError,
156
+ };
157
+ }
158
+ }
159
+ export function countGdcCheckIssues(data) {
160
+ const payload = asObject(data) || {};
161
+ const summary = asObject(payload.summary) || {};
162
+ const summaryErrors = asNumber(summary.error) ?? 0;
163
+ const summaryWarnings = asNumber(summary.warning) ?? 0;
164
+ const summaryInfos = asNumber(summary.info) ?? 0;
165
+ const issues = asArray(payload.issues);
166
+ let issueErrors = 0;
167
+ let issueWarnings = 0;
168
+ let issueInfos = 0;
169
+ for (const issue of issues) {
170
+ const item = asObject(issue);
171
+ const severity = String(item?.severity || '').toLowerCase();
172
+ if (severity === 'error')
173
+ issueErrors += 1;
174
+ else if (severity === 'warning' || severity === 'warn')
175
+ issueWarnings += 1;
176
+ else if (severity === 'info')
177
+ issueInfos += 1;
178
+ }
179
+ return {
180
+ errors: Math.max(summaryErrors, issueErrors),
181
+ warnings: Math.max(summaryWarnings, issueWarnings),
182
+ infos: Math.max(summaryInfos, issueInfos),
183
+ issueCount: issues.length,
184
+ };
185
+ }
186
+ export function getGraphNodeIds(data) {
187
+ const payload = asObject(data);
188
+ const nodes = asArray(payload?.nodes);
189
+ const ids = new Set();
190
+ for (const node of nodes) {
191
+ const item = asObject(node);
192
+ const id = typeof item?.id === 'string' ? item.id : '';
193
+ if (id)
194
+ ids.add(id);
195
+ }
196
+ return Array.from(ids);
197
+ }
198
+ export function getGraphEdges(data) {
199
+ const payload = asObject(data);
200
+ const edges = asArray(payload?.edges);
201
+ const result = [];
202
+ for (const edge of edges) {
203
+ const item = asObject(edge);
204
+ const from = typeof item?.from === 'string' ? item.from : '';
205
+ const to = typeof item?.to === 'string' ? item.to : '';
206
+ if (from && to)
207
+ result.push({ from, to });
208
+ }
209
+ return result;
210
+ }
211
+ export function getStatsNodeSummary(data) {
212
+ const payload = asObject(data);
213
+ const nodes = asObject(payload?.nodes);
214
+ const byStatus = asObject(nodes?.byStatus);
215
+ return {
216
+ total: asNumber(nodes?.total),
217
+ implemented: asNumber(byStatus?.implemented),
218
+ tested: asNumber(byStatus?.tested),
219
+ };
220
+ }
221
+ //# sourceMappingURL=gdc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gdc.js","sourceRoot":"","sources":["../../src/weave/gdc.ts"],"names":[],"mappings":"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"}
@@ -17,6 +17,6 @@ export interface ResearchResult {
17
17
  summary: string;
18
18
  report: string;
19
19
  }
20
- export declare function buildResearchReport(options: ResearchOptions): string;
20
+ export declare function buildResearchReport(options: ResearchOptions): Promise<string>;
21
21
  export declare function writeResearchReport(options: ResearchOptions): Promise<ResearchResult>;
22
22
  //# sourceMappingURL=research.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"research.d.ts","sourceRoot":"","sources":["../../../src/weave/stages/research.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,eAAe;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAClB;AAqfD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CA0FpE;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CA2B3F"}
1
+ {"version":3,"file":"research.d.ts","sourceRoot":"","sources":["../../../src/weave/stages/research.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAUhD,MAAM,WAAW,eAAe;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAClB;AA8tBD,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAgHnF;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CA2B3F"}
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import * as fs from 'node:fs';
8
8
  import * as path from 'node:path';
9
+ import { getEffectiveGdcConfig, runGdcMachineCommand, countGdcCheckIssues, getGraphNodeIds, getGraphEdges, getStatsNodeSummary, } from '../gdc.js';
9
10
  const WORKSPACE_IGNORED_DIRS = new Set([
10
11
  '.git',
11
12
  '.idea',
@@ -26,6 +27,10 @@ const WORKSPACE_IGNORED_DIRS = new Set([
26
27
  'target',
27
28
  'out',
28
29
  ]);
30
+ const WORKSPACE_ALLOWED_DOT_DIRS = new Set([
31
+ '.github',
32
+ '.gdc',
33
+ ]);
29
34
  const TEXT_EXTENSIONS = new Set([
30
35
  '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',
31
36
  '.json', '.yaml', '.yml', '.md', '.txt',
@@ -147,7 +152,7 @@ function collectWorkspaceFiles(basePath, maxFiles = MAX_WORKSPACE_FILES) {
147
152
  if (entry.isDirectory()) {
148
153
  if (WORKSPACE_IGNORED_DIRS.has(entry.name))
149
154
  continue;
150
- if (entry.name.startsWith('.') && entry.name !== '.github')
155
+ if (entry.name.startsWith('.') && !WORKSPACE_ALLOWED_DOT_DIRS.has(entry.name))
151
156
  continue;
152
157
  stack.push(fullPath);
153
158
  continue;
@@ -297,7 +302,199 @@ function findRelatedTests(testFiles, featureNeedles) {
297
302
  }
298
303
  return uniq(related).slice(0, 10);
299
304
  }
300
- function analyzeWorkspaceContext(basePath, intake) {
305
+ function listFilesRecursive(rootDir, exts) {
306
+ const files = [];
307
+ const stack = [rootDir];
308
+ while (stack.length > 0) {
309
+ const current = stack.pop();
310
+ let entries = [];
311
+ try {
312
+ entries = fs.readdirSync(current, { withFileTypes: true });
313
+ }
314
+ catch {
315
+ continue;
316
+ }
317
+ for (const entry of entries) {
318
+ const fullPath = path.join(current, entry.name);
319
+ if (entry.isSymbolicLink())
320
+ continue;
321
+ if (entry.isDirectory()) {
322
+ stack.push(fullPath);
323
+ continue;
324
+ }
325
+ if (!entry.isFile())
326
+ continue;
327
+ if (exts) {
328
+ const ext = path.extname(entry.name).toLowerCase();
329
+ if (!exts.has(ext))
330
+ continue;
331
+ }
332
+ files.push(fullPath);
333
+ }
334
+ }
335
+ return files;
336
+ }
337
+ function containsNeedle(target, needles) {
338
+ const lower = target.toLowerCase();
339
+ return needles.some(needle => lower.includes(needle));
340
+ }
341
+ async function analyzeGdcContext(basePath, intake) {
342
+ const gdcDir = path.join(basePath, '.gdc');
343
+ const configPath = path.join(gdcDir, 'config.yaml');
344
+ const graphDbPath = path.join(gdcDir, 'graph.db');
345
+ const nodesDir = path.join(gdcDir, 'nodes');
346
+ const hasGdcDir = fs.existsSync(gdcDir);
347
+ const hasConfig = fs.existsSync(configPath);
348
+ const hasNodes = fs.existsSync(nodesDir);
349
+ const hasGraphDb = fs.existsSync(graphDbPath);
350
+ const detected = hasGdcDir || hasConfig || hasNodes;
351
+ const gdcRuntime = getEffectiveGdcConfig(basePath);
352
+ if (!detected) {
353
+ return {
354
+ detected: false,
355
+ gateEnabled: gdcRuntime.enabled,
356
+ nodeSpecFiles: 0,
357
+ hasGraphDb: false,
358
+ sampleNodes: [],
359
+ machineSignals: [],
360
+ candidateReuseNodes: [],
361
+ dependencyBlastRadius: ['GDC metadata not detected in this workspace.'],
362
+ driftSignals: ['No GDC spec/graph available. Drift analysis skipped.'],
363
+ };
364
+ }
365
+ const nodeSpecFiles = hasNodes
366
+ ? listFilesRecursive(nodesDir, new Set(['.yaml', '.yml']))
367
+ : [];
368
+ const featureNeedles = buildFeatureNeedles(intake);
369
+ const sampleNodes = nodeSpecFiles
370
+ .map(file => toRelativePath(basePath, file))
371
+ .sort((a, b) => a.localeCompare(b))
372
+ .slice(0, 12);
373
+ let candidateReuseNodes = nodeSpecFiles
374
+ .filter(file => {
375
+ const normalized = file.replace(/\\/g, '/').toLowerCase();
376
+ return featureNeedles.some(needle => normalized.includes(needle));
377
+ })
378
+ .map(file => `\`${toRelativePath(basePath, file)}\``)
379
+ .slice(0, 12);
380
+ const machineSignals = [];
381
+ let statsResult = null;
382
+ let graphResult = null;
383
+ let checkResult = null;
384
+ if (gdcRuntime.enabled) {
385
+ [statsResult, graphResult, checkResult] = await Promise.all([
386
+ runGdcMachineCommand({
387
+ basePath,
388
+ command: 'stats',
389
+ config: gdcRuntime,
390
+ timeoutMs: 30_000,
391
+ }),
392
+ runGdcMachineCommand({
393
+ basePath,
394
+ command: 'graph',
395
+ args: ['--format', 'json'],
396
+ config: gdcRuntime,
397
+ timeoutMs: 45_000,
398
+ }),
399
+ runGdcMachineCommand({
400
+ basePath,
401
+ command: 'check',
402
+ config: gdcRuntime,
403
+ timeoutMs: 45_000,
404
+ }),
405
+ ]);
406
+ machineSignals.push(`stats exit=${statsResult.exitCode}`);
407
+ machineSignals.push(`graph exit=${graphResult.exitCode}`);
408
+ machineSignals.push(`check exit=${checkResult.exitCode}`);
409
+ const statsSummary = getStatsNodeSummary(statsResult.data);
410
+ if (typeof statsSummary.total === 'number') {
411
+ machineSignals.push(`stats nodes.total=${statsSummary.total}`);
412
+ }
413
+ const graphNodeIds = getGraphNodeIds(graphResult.data);
414
+ if (graphNodeIds.length > 0) {
415
+ machineSignals.push(`graph nodes=${graphNodeIds.length}`);
416
+ }
417
+ const graphMatchedNodes = graphNodeIds
418
+ .filter(nodeId => containsNeedle(nodeId, featureNeedles))
419
+ .slice(0, 12)
420
+ .map(nodeId => `\`${nodeId}\``);
421
+ if (graphMatchedNodes.length > 0) {
422
+ candidateReuseNodes = uniq([...graphMatchedNodes, ...candidateReuseNodes]).slice(0, 12);
423
+ }
424
+ const checkCounts = countGdcCheckIssues(checkResult.data);
425
+ if (checkCounts.issueCount > 0 || checkResult.exitCode === 2) {
426
+ machineSignals.push(`check issues=${checkCounts.issueCount}`);
427
+ }
428
+ }
429
+ let dependencyBlastRadius = candidateReuseNodes.length > 0
430
+ ? [
431
+ `Candidate node specs touching requested features: ${candidateReuseNodes.slice(0, 5).join(', ')}`,
432
+ 'Run `gdc trace <node> --direction both --machine` to measure full dependency impact.',
433
+ ]
434
+ : [
435
+ 'No direct node filename matches found for requested features.',
436
+ 'Run `gdc query <feature>` then `gdc trace <node> --direction both --machine` for graph-level impact.',
437
+ ];
438
+ if (graphResult) {
439
+ const edges = getGraphEdges(graphResult.data);
440
+ if (edges.length > 0 && candidateReuseNodes.length > 0) {
441
+ const degree = new Map();
442
+ for (const edge of edges) {
443
+ degree.set(edge.from, (degree.get(edge.from) || 0) + 1);
444
+ degree.set(edge.to, (degree.get(edge.to) || 0) + 1);
445
+ }
446
+ const ranked = candidateReuseNodes
447
+ .map(token => token.replace(/`/g, ''))
448
+ .map(nodeId => ({ nodeId, score: degree.get(nodeId) || 0 }))
449
+ .sort((a, b) => b.score - a.score || a.nodeId.localeCompare(b.nodeId))
450
+ .slice(0, 5);
451
+ if (ranked.length > 0) {
452
+ dependencyBlastRadius = [
453
+ `Graph neighbors for candidate nodes: ${ranked.map(item => `\`${item.nodeId}\`(${item.score})`).join(', ')}`,
454
+ 'Higher degree nodes usually imply broader dependency impact.',
455
+ ];
456
+ }
457
+ }
458
+ }
459
+ const driftSignals = [];
460
+ if (!hasConfig)
461
+ driftSignals.push('`.gdc/config.yaml` missing: spec/runtime alignment checks may be incomplete.');
462
+ if (!hasGraphDb)
463
+ driftSignals.push('`.gdc/graph.db` missing: run `gdc sync` before `gdc check`.');
464
+ if (nodeSpecFiles.length === 0)
465
+ driftSignals.push('No `.gdc/nodes/*.yaml` specs found.');
466
+ if (checkResult) {
467
+ const checkCounts = countGdcCheckIssues(checkResult.data);
468
+ if (checkResult.exitCode === 2 || checkCounts.errors > 0) {
469
+ driftSignals.push(`GDC check reports blocking issues (errors=${checkCounts.errors}, warnings=${checkCounts.warnings}).`);
470
+ }
471
+ else if (checkResult.exitCode === 0) {
472
+ driftSignals.push(`GDC check clean (errors=${checkCounts.errors}, warnings=${checkCounts.warnings}, info=${checkCounts.infos}).`);
473
+ }
474
+ else {
475
+ driftSignals.push(`GDC check command failed (exit=${checkResult.exitCode}).`);
476
+ }
477
+ if (checkResult.parseError) {
478
+ driftSignals.push(`Machine output parse warning: ${normalizeLine(checkResult.parseError, 120)}`);
479
+ }
480
+ }
481
+ if (driftSignals.length === 0) {
482
+ driftSignals.push('GDC metadata detected. Run `gdc check --machine` to confirm spec/implementation drift.');
483
+ }
484
+ return {
485
+ detected: true,
486
+ gateEnabled: gdcRuntime.enabled,
487
+ configPath: hasConfig ? toRelativePath(basePath, configPath) : undefined,
488
+ nodeSpecFiles: nodeSpecFiles.length,
489
+ hasGraphDb,
490
+ sampleNodes,
491
+ machineSignals,
492
+ candidateReuseNodes,
493
+ dependencyBlastRadius,
494
+ driftSignals,
495
+ };
496
+ }
497
+ async function analyzeWorkspaceContext(basePath, intake) {
301
498
  const workspaceFiles = collectWorkspaceFiles(basePath);
302
499
  const codeFiles = workspaceFiles.filter(isCodeFile);
303
500
  const testFiles = workspaceFiles.filter(isTestFile);
@@ -416,7 +613,9 @@ function analyzeWorkspaceContext(basePath, intake) {
416
613
  'next.config.js',
417
614
  'docker-compose.yml',
418
615
  '.github/workflows',
616
+ '.gdc/config.yaml',
419
617
  ].filter(rel => fs.existsSync(path.join(basePath, rel)));
618
+ const gdc = await analyzeGdcContext(basePath, intake);
420
619
  return {
421
620
  scannedFiles: workspaceFiles.length,
422
621
  codeFiles: codeFiles.length,
@@ -429,9 +628,10 @@ function analyzeWorkspaceContext(basePath, intake) {
429
628
  reproductionFlow,
430
629
  beforeContext,
431
630
  afterContext,
631
+ gdc,
432
632
  };
433
633
  }
434
- export function buildResearchReport(options) {
634
+ export async function buildResearchReport(options) {
435
635
  const basePath = options.basePath || process.cwd();
436
636
  const now = new Date().toISOString();
437
637
  const featureLines = uniq(top(options.intake.features, 12));
@@ -456,7 +656,7 @@ export function buildResearchReport(options) {
456
656
  const envIssues = top(options.intake.environment?.issues || [], 10).map(issue => {
457
657
  return `${issue.severity.toUpperCase()} - ${issue.title}: ${issue.prevention}`;
458
658
  });
459
- const workspace = analyzeWorkspaceContext(basePath, options.intake);
659
+ const workspace = await analyzeWorkspaceContext(basePath, options.intake);
460
660
  const workspaceScope = [
461
661
  `Scanned files: ${workspace.scannedFiles} (code: ${workspace.codeFiles}, tests: ${workspace.testFiles})`,
462
662
  `Research scope: current workspace only (\`${toRelativePath(basePath, basePath)}\`)`,
@@ -472,6 +672,21 @@ export function buildResearchReport(options) {
472
672
  const snippet = candidate.snippet ? ` | ${candidate.snippet}` : '';
473
673
  return `\`${rel}\` (${featurePart})${snippet}`;
474
674
  });
675
+ const gdcNodeCoverage = workspace.gdc.detected
676
+ ? [
677
+ `Detected: yes`,
678
+ `Gate enabled: ${workspace.gdc.gateEnabled ? 'yes' : 'no'}`,
679
+ `Node specs: ${workspace.gdc.nodeSpecFiles}`,
680
+ `Graph DB present: ${workspace.gdc.hasGraphDb ? 'yes' : 'no'}`,
681
+ ...(workspace.gdc.configPath ? [`Config: \`${workspace.gdc.configPath}\``] : ['Config: (missing)']),
682
+ ...(workspace.gdc.sampleNodes.length > 0
683
+ ? [`Sample nodes: ${workspace.gdc.sampleNodes.map(node => `\`${node}\``).join(', ')}`]
684
+ : ['Sample nodes: (none)']),
685
+ ]
686
+ : ['Detected: no', 'No `.gdc` metadata found in workspace.'];
687
+ const gdcMachineSignals = workspace.gdc.machineSignals.length > 0
688
+ ? workspace.gdc.machineSignals
689
+ : ['No GDC machine-command data collected.'];
475
690
  const lines = [];
476
691
  lines.push('# Weave Research Report');
477
692
  lines.push('');
@@ -500,6 +715,11 @@ export function buildResearchReport(options) {
500
715
  formatList(lines, 'Problem Reproduction Flow', workspace.reproductionFlow);
501
716
  formatList(lines, 'Before Context (Current State)', workspace.beforeContext);
502
717
  formatList(lines, 'After Context (Target Intent)', workspace.afterContext);
718
+ formatList(lines, 'GDC Node Coverage', gdcNodeCoverage);
719
+ formatList(lines, 'GDC Machine Signals', gdcMachineSignals);
720
+ formatList(lines, 'Dependency Blast Radius', workspace.gdc.dependencyBlastRadius);
721
+ formatList(lines, 'Existing Spec vs Implementation Drift', workspace.gdc.driftSignals);
722
+ formatList(lines, 'Candidate Reuse Nodes', workspace.gdc.candidateReuseNodes);
503
723
  lines.push('## Suggested Next Steps');
504
724
  lines.push('');
505
725
  lines.push('1. Preserve reuse candidates first; avoid implementing duplicates unless behavior diverges.');
@@ -515,7 +735,7 @@ export async function writeResearchReport(options) {
515
735
  const outputPath = options.outputPath
516
736
  ? (path.isAbsolute(options.outputPath) ? options.outputPath : path.join(basePath, options.outputPath))
517
737
  : path.join(tasksDir, 'research.md');
518
- const report = buildResearchReport(options);
738
+ const report = await buildResearchReport(options);
519
739
  fs.writeFileSync(outputPath, `${report}\n`, 'utf-8');
520
740
  const rel = toRelativePath(basePath, outputPath);
521
741
  const summary = [