@neurcode-ai/cli 0.18.0 ā 0.19.2
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/commands/brain.d.ts.map +1 -1
- package/dist/commands/brain.js +537 -6
- package/dist/commands/brain.js.map +1 -1
- package/dist/commands/ops.d.ts +5 -0
- package/dist/commands/ops.d.ts.map +1 -1
- package/dist/commands/ops.js +32 -2
- package/dist/commands/ops.js.map +1 -1
- package/dist/commands/policy.d.ts.map +1 -1
- package/dist/commands/policy.js +346 -0
- package/dist/commands/policy.js.map +1 -1
- package/dist/commands/quickstart.d.ts.map +1 -1
- package/dist/commands/quickstart.js +11 -6
- package/dist/commands/quickstart.js.map +1 -1
- package/dist/commands/runtime-adapter.d.ts +2 -1
- package/dist/commands/runtime-adapter.d.ts.map +1 -1
- package/dist/commands/runtime-adapter.js +51 -2
- package/dist/commands/runtime-adapter.js.map +1 -1
- package/dist/commands/session-hook.d.ts +13 -0
- package/dist/commands/session-hook.d.ts.map +1 -1
- package/dist/commands/session-hook.js +115 -15
- package/dist/commands/session-hook.js.map +1 -1
- package/dist/commands/session.d.ts +5 -0
- package/dist/commands/session.d.ts.map +1 -1
- package/dist/commands/session.js +328 -53
- package/dist/commands/session.js.map +1 -1
- package/dist/commands/verify-output.d.ts +2 -0
- package/dist/commands/verify-output.d.ts.map +1 -1
- package/dist/commands/verify-output.js +4 -0
- package/dist/commands/verify-output.js.map +1 -1
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +108 -24
- package/dist/commands/verify.js.map +1 -1
- package/dist/governance/structural-on-diff.d.ts +11 -0
- package/dist/governance/structural-on-diff.d.ts.map +1 -1
- package/dist/governance/structural-on-diff.js +38 -5
- package/dist/governance/structural-on-diff.js.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/runtime-build.json +5 -5
- package/dist/utils/agent-adapter-setup.js +1 -1
- package/dist/utils/agent-adapter-setup.js.map +1 -1
- package/dist/utils/agent-guard.d.ts +1 -0
- package/dist/utils/agent-guard.d.ts.map +1 -1
- package/dist/utils/agent-guard.js +18 -6
- package/dist/utils/agent-guard.js.map +1 -1
- package/dist/utils/brain-context.d.ts.map +1 -1
- package/dist/utils/brain-context.js +11 -2
- package/dist/utils/brain-context.js.map +1 -1
- package/dist/utils/git-coverage.d.ts.map +1 -1
- package/dist/utils/git-coverage.js +1 -0
- package/dist/utils/git-coverage.js.map +1 -1
- package/dist/utils/local-repo-brain.d.ts +7 -0
- package/dist/utils/local-repo-brain.d.ts.map +1 -1
- package/dist/utils/local-repo-brain.js +22 -5
- package/dist/utils/local-repo-brain.js.map +1 -1
- package/dist/utils/proposed-change-analysis.d.ts +20 -0
- package/dist/utils/proposed-change-analysis.d.ts.map +1 -0
- package/dist/utils/proposed-change-analysis.js +450 -0
- package/dist/utils/proposed-change-analysis.js.map +1 -0
- package/dist/utils/repo-intelligence-v2.d.ts +28 -0
- package/dist/utils/repo-intelligence-v2.d.ts.map +1 -0
- package/dist/utils/repo-intelligence-v2.js +215 -0
- package/dist/utils/repo-intelligence-v2.js.map +1 -0
- package/dist/utils/structural-understanding.d.ts +2 -2
- package/dist/utils/structural-understanding.d.ts.map +1 -1
- package/dist/utils/structural-understanding.js +1 -1
- package/dist/utils/structural-understanding.js.map +1 -1
- package/dist/utils/team-memory-path-hygiene.d.ts +4 -0
- package/dist/utils/team-memory-path-hygiene.d.ts.map +1 -0
- package/dist/utils/team-memory-path-hygiene.js +50 -0
- package/dist/utils/team-memory-path-hygiene.js.map +1 -0
- package/dist/utils/v0-governance.d.ts +8 -1
- package/dist/utils/v0-governance.d.ts.map +1 -1
- package/dist/utils/v0-governance.js +96 -17
- package/dist/utils/v0-governance.js.map +1 -1
- package/package.json +6 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"brain.d.ts","sourceRoot":"","sources":["../../src/commands/brain.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"brain.d.ts","sourceRoot":"","sources":["../../src/commands/brain.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAyfpC,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAy/DnD"}
|
package/dist/commands/brain.js
CHANGED
|
@@ -45,6 +45,7 @@ exports.brainCommand = brainCommand;
|
|
|
45
45
|
const child_process_1 = require("child_process");
|
|
46
46
|
const fs_1 = require("fs");
|
|
47
47
|
const path_1 = require("path");
|
|
48
|
+
const brain_1 = require("@neurcode-ai/brain");
|
|
48
49
|
const brain_cache_1 = require("../utils/brain-cache");
|
|
49
50
|
const local_repo_brain_1 = require("../utils/local-repo-brain");
|
|
50
51
|
const repo_brain_impact_1 = require("../utils/repo-brain-impact");
|
|
@@ -57,6 +58,8 @@ const messages_1 = require("../utils/messages");
|
|
|
57
58
|
const brain_context_1 = require("../utils/brain-context");
|
|
58
59
|
const semantic_1 = require("../semantic");
|
|
59
60
|
const ask_cache_1 = require("../utils/ask-cache");
|
|
61
|
+
const proposed_change_analysis_1 = require("../utils/proposed-change-analysis");
|
|
62
|
+
const team_memory_path_hygiene_1 = require("../utils/team-memory-path-hygiene");
|
|
60
63
|
// Import chalk with fallback
|
|
61
64
|
let chalk;
|
|
62
65
|
try {
|
|
@@ -124,6 +127,9 @@ function scanFiles(dir, baseDir, maxFiles = 600) {
|
|
|
124
127
|
continue;
|
|
125
128
|
}
|
|
126
129
|
const full = join(current, entry);
|
|
130
|
+
const relativePath = normalizeFsPath(relative(baseDir, full));
|
|
131
|
+
if (!(0, team_memory_path_hygiene_1.isTeamMemoryProjectPath)(relativePath))
|
|
132
|
+
continue;
|
|
127
133
|
let st;
|
|
128
134
|
try {
|
|
129
135
|
st = statSync(full);
|
|
@@ -142,7 +148,7 @@ function scanFiles(dir, baseDir, maxFiles = 600) {
|
|
|
142
148
|
const ext = entry.split('.').pop()?.toLowerCase();
|
|
143
149
|
if (ext && ignoreExts.has(ext))
|
|
144
150
|
continue;
|
|
145
|
-
files.push(
|
|
151
|
+
files.push(relativePath);
|
|
146
152
|
}
|
|
147
153
|
}
|
|
148
154
|
walk(dir);
|
|
@@ -226,7 +232,7 @@ function collectGitAuthorship(cwd, sinceDays) {
|
|
|
226
232
|
if (!currentAuthor)
|
|
227
233
|
continue;
|
|
228
234
|
const path = normalizeFsPath(line);
|
|
229
|
-
if (!path || path.startsWith('.git/') || path.startsWith('node_modules/'))
|
|
235
|
+
if (!path || path.startsWith('.git/') || path.startsWith('node_modules/') || !(0, team_memory_path_hygiene_1.isTeamMemoryProjectPath)(path))
|
|
230
236
|
continue;
|
|
231
237
|
authorTouches.set(currentAuthor, (authorTouches.get(currentAuthor) || 0) + 1);
|
|
232
238
|
const byAuthor = fileTouches.get(path) || new Map();
|
|
@@ -280,6 +286,86 @@ function splitChangedPathList(value) {
|
|
|
280
286
|
.map((path) => path.trim())
|
|
281
287
|
.filter(Boolean);
|
|
282
288
|
}
|
|
289
|
+
function parseRenameList(value) {
|
|
290
|
+
return splitChangedPathList(value)
|
|
291
|
+
.map((entry) => {
|
|
292
|
+
const delimiter = entry.includes('=>') ? '=>' : ':';
|
|
293
|
+
const [from, to] = entry.split(delimiter, 2).map((item) => item.trim());
|
|
294
|
+
return from && to ? { from, to } : null;
|
|
295
|
+
})
|
|
296
|
+
.filter((entry) => Boolean(entry));
|
|
297
|
+
}
|
|
298
|
+
function repositoryGraphError(error, json) {
|
|
299
|
+
const locked = error instanceof brain_1.RepositoryGraphLockedError;
|
|
300
|
+
const payload = {
|
|
301
|
+
ok: false,
|
|
302
|
+
code: locked ? 'repository_graph_locked' : 'repository_graph_failed',
|
|
303
|
+
message: error instanceof Error ? error.message : String(error),
|
|
304
|
+
exitCode: locked ? 3 : 1,
|
|
305
|
+
};
|
|
306
|
+
if (json) {
|
|
307
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
(0, messages_1.printError)('Repository Graph V2 operation failed', payload.message);
|
|
311
|
+
}
|
|
312
|
+
process.exitCode = payload.exitCode;
|
|
313
|
+
}
|
|
314
|
+
function printRepositoryGraphIndexResult(repoRoot, result, json) {
|
|
315
|
+
const payload = {
|
|
316
|
+
ok: true,
|
|
317
|
+
graphPath: (0, brain_1.repositoryGraphPath)(repoRoot),
|
|
318
|
+
schemaVersion: result.graph.schemaVersion,
|
|
319
|
+
graphId: result.graph.graphId,
|
|
320
|
+
generation: result.graph.generation,
|
|
321
|
+
freshness: result.graph.freshness,
|
|
322
|
+
coverage: result.graph.coverage,
|
|
323
|
+
stats: result.stats,
|
|
324
|
+
privacy: result.graph.privacy,
|
|
325
|
+
};
|
|
326
|
+
if (json) {
|
|
327
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
console.log(chalk.bold('\nš§ Repository Graph V2\n'));
|
|
331
|
+
console.log(chalk.dim(`Mode: ${result.stats.mode}`));
|
|
332
|
+
console.log(chalk.dim(`Generation: ${result.graph.generation}`));
|
|
333
|
+
console.log(chalk.dim(`Freshness: ${result.graph.freshness.state}`));
|
|
334
|
+
console.log(chalk.dim(`Files indexed: ${result.graph.coverage.filesIndexed}`));
|
|
335
|
+
console.log(chalk.dim(`Files parsed: ${result.stats.filesParsed}`));
|
|
336
|
+
console.log(chalk.dim(`Files reused: ${result.stats.filesReused}`));
|
|
337
|
+
console.log(chalk.dim(`Unsupported: ${result.graph.coverage.unsupportedPercent}%`));
|
|
338
|
+
console.log(chalk.dim(`Nodes / edges: ${result.graph.nodes.length} / ${result.graph.edges.length}`));
|
|
339
|
+
console.log(chalk.dim(`Graph size: ${formatBytes(result.stats.graphBytes)}`));
|
|
340
|
+
console.log(chalk.dim(`Duration: ${result.stats.durationMs}ms`));
|
|
341
|
+
console.log(chalk.dim(`Peak RSS: ${result.stats.peakMemoryMb} MB`));
|
|
342
|
+
console.log(chalk.dim(`Artifact: ${payload.graphPath}`));
|
|
343
|
+
console.log(chalk.dim('Privacy: source-free local structural facts; raw source is not retained.'));
|
|
344
|
+
}
|
|
345
|
+
function normalizeAdvisoryTarget(repoRoot, input) {
|
|
346
|
+
const absolutePath = (0, path_1.isAbsolute)(input) ? (0, path_1.resolve)(input) : (0, path_1.resolve)(repoRoot, input);
|
|
347
|
+
const relativePath = (0, path_1.relative)(repoRoot, absolutePath).replace(/\\/g, '/');
|
|
348
|
+
if (!relativePath || relativePath === '..' || relativePath.startsWith('../')) {
|
|
349
|
+
throw new Error(`Path is outside repository root: ${input}`);
|
|
350
|
+
}
|
|
351
|
+
return { relativePath, absolutePath };
|
|
352
|
+
}
|
|
353
|
+
function advisoryCategory(value) {
|
|
354
|
+
const categories = [
|
|
355
|
+
'behavior_similarity',
|
|
356
|
+
'reuse_suggestion',
|
|
357
|
+
'duplicate_module',
|
|
358
|
+
'architecture_deviation',
|
|
359
|
+
'cross_service_consequence',
|
|
360
|
+
'reviewer_question',
|
|
361
|
+
'missing_test',
|
|
362
|
+
'ownership_review',
|
|
363
|
+
];
|
|
364
|
+
if (!categories.includes(value)) {
|
|
365
|
+
throw new Error(`Unsupported advisory category: ${value}`);
|
|
366
|
+
}
|
|
367
|
+
return value;
|
|
368
|
+
}
|
|
283
369
|
function renderBrainExportMarkdown(input) {
|
|
284
370
|
const lines = [];
|
|
285
371
|
lines.push('# Neurcode Brain Export');
|
|
@@ -539,6 +625,13 @@ function brainCommand(program) {
|
|
|
539
625
|
.option('--json', 'Output as JSON')
|
|
540
626
|
.action(async (options) => {
|
|
541
627
|
const scope = getBrainScope();
|
|
628
|
+
const canonical = await (0, brain_1.indexRepositoryGraph)({
|
|
629
|
+
repoRoot: scope.cwd,
|
|
630
|
+
limits: {
|
|
631
|
+
...(Number.isFinite(options.maxFiles) ? { maxFiles: options.maxFiles } : {}),
|
|
632
|
+
...(Number.isFinite(options.maxBytesPerFile) ? { maxBytesPerFile: options.maxBytesPerFile } : {}),
|
|
633
|
+
},
|
|
634
|
+
});
|
|
542
635
|
const artifact = (0, local_repo_brain_1.buildLocalRepoBrain)(scope.cwd, {
|
|
543
636
|
maxFiles: options.maxFiles,
|
|
544
637
|
maxBytesPerFile: options.maxBytesPerFile,
|
|
@@ -547,6 +640,24 @@ function brainCommand(program) {
|
|
|
547
640
|
const paths = (0, local_repo_brain_1.writeLocalRepoBrain)(scope.cwd, artifact);
|
|
548
641
|
const payload = {
|
|
549
642
|
repoRoot: scope.cwd,
|
|
643
|
+
repositoryIntelligenceModel: {
|
|
644
|
+
canonicalLifecycle: 'repository_graph_v2',
|
|
645
|
+
surface: 'legacy_brain_compatibility_projection',
|
|
646
|
+
compatibilityOnly: true,
|
|
647
|
+
},
|
|
648
|
+
canonicalGraph: {
|
|
649
|
+
path: (0, brain_1.repositoryGraphPath)(scope.cwd),
|
|
650
|
+
graphId: canonical.graph.graphId,
|
|
651
|
+
schemaVersion: canonical.graph.schemaVersion,
|
|
652
|
+
freshness: canonical.graph.freshness,
|
|
653
|
+
coverage: canonical.graph.coverage,
|
|
654
|
+
nodeCount: canonical.graph.nodes.length,
|
|
655
|
+
edgeCount: canonical.graph.edges.length,
|
|
656
|
+
},
|
|
657
|
+
compatibilityProjection: {
|
|
658
|
+
artifactHash: artifact.artifactHash,
|
|
659
|
+
summary: artifact.summary,
|
|
660
|
+
},
|
|
550
661
|
jsonPath: paths.jsonPath,
|
|
551
662
|
markdownPath: paths.markdownPath,
|
|
552
663
|
artifactHash: artifact.artifactHash,
|
|
@@ -559,8 +670,14 @@ function brainCommand(program) {
|
|
|
559
670
|
console.log(JSON.stringify(payload, null, 2));
|
|
560
671
|
return;
|
|
561
672
|
}
|
|
562
|
-
await (0, messages_1.printSuccessBanner)('
|
|
673
|
+
await (0, messages_1.printSuccessBanner)('Canonical Repository Intelligence Indexed');
|
|
674
|
+
(0, messages_1.printSection)('Canonical lifecycle', 'š§');
|
|
675
|
+
console.log(chalk.dim(`Repository Graph: ${payload.canonicalGraph.graphId}`));
|
|
676
|
+
console.log(chalk.dim(`Posture: ${canonical.graph.freshness.posture ?? canonical.graph.freshness.state}`));
|
|
677
|
+
console.log(chalk.dim(`Analyzed/skipped: ${canonical.graph.coverage.filesAnalyzed}/${canonical.graph.coverage.filesSkipped}`));
|
|
678
|
+
console.log(chalk.dim(`Recovery: neurcode brain repo-recover`));
|
|
563
679
|
(0, messages_1.printSection)('Artifact', 'š§ ');
|
|
680
|
+
console.log(chalk.yellow('Legacy Brain is a compatibility projection; its counts are not canonical lifecycle health.'));
|
|
564
681
|
console.log(chalk.dim(`Repo Root: ${scope.cwd}`));
|
|
565
682
|
console.log(chalk.dim(`JSON: ${paths.jsonPath}`));
|
|
566
683
|
console.log(chalk.dim(`Summary: ${paths.markdownPath}`));
|
|
@@ -595,6 +712,379 @@ function brainCommand(program) {
|
|
|
595
712
|
}
|
|
596
713
|
(0, messages_1.printInfo)('Next', 'Run: neurcode brain inspect "<area or symbol>"');
|
|
597
714
|
});
|
|
715
|
+
// -- Repository Graph V2 ---------------------------------------------------
|
|
716
|
+
brain
|
|
717
|
+
.command('repo-index')
|
|
718
|
+
.description('Create or incrementally refresh the persistent Repository Graph V2')
|
|
719
|
+
.option('--changed <paths>', 'Changed paths separated by commas, spaces, or newlines')
|
|
720
|
+
.option('--deleted <paths>', 'Deleted paths separated by commas, spaces, or newlines')
|
|
721
|
+
.option('--rename <pairs>', 'Rename pairs as old:new or old=>new, separated by commas')
|
|
722
|
+
.option('--max-files <n>', 'Maximum files to inspect', (value) => parseInt(value, 10))
|
|
723
|
+
.option('--max-total-bytes <n>', 'Maximum total bytes to inspect', (value) => parseInt(value, 10))
|
|
724
|
+
.option('--max-bytes-per-file <n>', 'Maximum bytes per file', (value) => parseInt(value, 10))
|
|
725
|
+
.option('--json', 'Output stable machine-readable JSON')
|
|
726
|
+
.action(async (options) => {
|
|
727
|
+
const scope = getBrainScope();
|
|
728
|
+
try {
|
|
729
|
+
const limits = {
|
|
730
|
+
...(Number.isFinite(options.maxFiles) ? { maxFiles: options.maxFiles } : {}),
|
|
731
|
+
...(Number.isFinite(options.maxTotalBytes) ? { maxTotalBytes: options.maxTotalBytes } : {}),
|
|
732
|
+
...(Number.isFinite(options.maxBytesPerFile) ? { maxBytesPerFile: options.maxBytesPerFile } : {}),
|
|
733
|
+
};
|
|
734
|
+
const result = await (0, brain_1.indexRepositoryGraph)({
|
|
735
|
+
repoRoot: scope.cwd,
|
|
736
|
+
changedPaths: splitChangedPathList(options.changed),
|
|
737
|
+
deletedPaths: splitChangedPathList(options.deleted),
|
|
738
|
+
renamedPaths: parseRenameList(options.rename),
|
|
739
|
+
limits,
|
|
740
|
+
});
|
|
741
|
+
printRepositoryGraphIndexResult(scope.cwd, result, options.json);
|
|
742
|
+
}
|
|
743
|
+
catch (error) {
|
|
744
|
+
repositoryGraphError(error, options.json);
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
brain
|
|
748
|
+
.command('repo-refresh')
|
|
749
|
+
.description('Refresh stale Repository Graph V2 files using content hashes')
|
|
750
|
+
.option('--changed <paths>', 'Optional changed paths separated by commas, spaces, or newlines')
|
|
751
|
+
.option('--deleted <paths>', 'Optional deleted paths separated by commas, spaces, or newlines')
|
|
752
|
+
.option('--rename <pairs>', 'Optional rename pairs as old:new or old=>new')
|
|
753
|
+
.option('--json', 'Output stable machine-readable JSON')
|
|
754
|
+
.action(async (options) => {
|
|
755
|
+
const scope = getBrainScope();
|
|
756
|
+
try {
|
|
757
|
+
const result = await (0, brain_1.indexRepositoryGraph)({
|
|
758
|
+
repoRoot: scope.cwd,
|
|
759
|
+
changedPaths: splitChangedPathList(options.changed),
|
|
760
|
+
deletedPaths: splitChangedPathList(options.deleted),
|
|
761
|
+
renamedPaths: parseRenameList(options.rename),
|
|
762
|
+
});
|
|
763
|
+
printRepositoryGraphIndexResult(scope.cwd, result, options.json);
|
|
764
|
+
}
|
|
765
|
+
catch (error) {
|
|
766
|
+
repositoryGraphError(error, options.json);
|
|
767
|
+
}
|
|
768
|
+
});
|
|
769
|
+
brain
|
|
770
|
+
.command('repo-status')
|
|
771
|
+
.description('Show Repository Graph V2 freshness, coverage, and parser depth')
|
|
772
|
+
.option('--json', 'Output stable machine-readable JSON')
|
|
773
|
+
.action(async (options) => {
|
|
774
|
+
const scope = getBrainScope();
|
|
775
|
+
try {
|
|
776
|
+
const freshness = await (0, brain_1.repositoryGraphStatus)(scope.cwd);
|
|
777
|
+
const graph = (0, brain_1.readRepositoryGraph)(scope.cwd);
|
|
778
|
+
const payload = {
|
|
779
|
+
ok: freshness.state !== 'corrupt',
|
|
780
|
+
repoRoot: scope.cwd,
|
|
781
|
+
graphPath: (0, brain_1.repositoryGraphPath)(scope.cwd),
|
|
782
|
+
freshness,
|
|
783
|
+
graph: graph ? {
|
|
784
|
+
schemaVersion: graph.schemaVersion,
|
|
785
|
+
graphId: graph.graphId,
|
|
786
|
+
generation: graph.generation,
|
|
787
|
+
updatedAt: graph.updatedAt,
|
|
788
|
+
coverage: graph.coverage,
|
|
789
|
+
nodeCount: graph.nodes.length,
|
|
790
|
+
edgeCount: graph.edges.length,
|
|
791
|
+
privacy: graph.privacy,
|
|
792
|
+
} : null,
|
|
793
|
+
};
|
|
794
|
+
if (options.json) {
|
|
795
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
console.log(chalk.bold('\nš§ Repository Graph V2 Status\n'));
|
|
799
|
+
console.log(chalk.dim(`State: ${freshness.state}`));
|
|
800
|
+
console.log(chalk.dim(`Indexed at: ${freshness.indexedAt ?? 'never'}`));
|
|
801
|
+
console.log(chalk.dim(`Stale files: ${freshness.staleFileCount}`));
|
|
802
|
+
console.log(chalk.dim(`Unsupported files: ${freshness.unsupportedFileCount}`));
|
|
803
|
+
console.log(chalk.dim(`Reason codes: ${freshness.reasonCodes.join(', ') || 'none'}`));
|
|
804
|
+
if (graph) {
|
|
805
|
+
console.log(chalk.dim(`Generation: ${graph.generation}`));
|
|
806
|
+
console.log(chalk.dim(`Nodes / edges: ${graph.nodes.length} / ${graph.edges.length}`));
|
|
807
|
+
console.log(chalk.dim(`Unsupported: ${graph.coverage.unsupportedPercent}%`));
|
|
808
|
+
for (const language of graph.coverage.languages) {
|
|
809
|
+
console.log(chalk.dim(` ${language.language}: ${language.depth}; ${language.filesAnalyzed}/${language.filesSeen} analyzed`));
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
else {
|
|
813
|
+
console.log(chalk.dim('Run `neurcode brain repo-index` to create the graph.'));
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
catch (error) {
|
|
817
|
+
repositoryGraphError(error, options.json);
|
|
818
|
+
}
|
|
819
|
+
});
|
|
820
|
+
brain
|
|
821
|
+
.command('repo-explain <query...>')
|
|
822
|
+
.description('Explain a source-free path, symbol, package, service, or surface in Repository Graph V2')
|
|
823
|
+
.option('--limit <n>', 'Maximum matching nodes (default: 25)', (value) => parseInt(value, 10))
|
|
824
|
+
.option('--json', 'Output stable machine-readable JSON')
|
|
825
|
+
.action((queryParts, options) => {
|
|
826
|
+
const scope = getBrainScope();
|
|
827
|
+
const graph = (0, brain_1.readRepositoryGraph)(scope.cwd);
|
|
828
|
+
if (!graph) {
|
|
829
|
+
repositoryGraphError(new Error('Repository Graph V2 is missing or corrupt. Run `neurcode brain repo-index`.'), options.json);
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
const query = queryParts.join(' ').trim();
|
|
833
|
+
const matches = (0, brain_1.explainRepositoryGraph)(graph, query, options.limit);
|
|
834
|
+
const matchIds = new Set(matches.map((node) => node.id));
|
|
835
|
+
const edges = graph.edges.filter((edge) => matchIds.has(edge.fromId) || matchIds.has(edge.toId));
|
|
836
|
+
const payload = { ok: true, query, matches, edges, freshness: graph.freshness };
|
|
837
|
+
if (options.json) {
|
|
838
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
console.log(chalk.bold(`\nš§ Repository Graph V2: ${query}\n`));
|
|
842
|
+
if (matches.length === 0) {
|
|
843
|
+
console.log(chalk.dim('No source-free graph facts matched.'));
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
matches.forEach((node, index) => {
|
|
847
|
+
console.log(chalk.white(` ${index + 1}. [${node.kind}] ${node.name ?? node.key}`));
|
|
848
|
+
if (node.path)
|
|
849
|
+
console.log(chalk.dim(` path: ${node.path}`));
|
|
850
|
+
console.log(chalk.dim(` parser: ${node.provenance.parserDepth}`));
|
|
851
|
+
});
|
|
852
|
+
console.log(chalk.dim(`Related edges: ${edges.length}`));
|
|
853
|
+
});
|
|
854
|
+
brain
|
|
855
|
+
.command('repo-query')
|
|
856
|
+
.description('Query deterministic Repository Graph V2 references, dependencies, imports, calls, tests, or boundaries')
|
|
857
|
+
.option('--path <path>', 'Seed path or path fragment')
|
|
858
|
+
.option('--symbol <name>', 'Seed symbol name or fragment')
|
|
859
|
+
.option('--relationship <type>', 'Edge type such as references, depends_on, imports, calls, tests, or crosses_boundary')
|
|
860
|
+
.option('--direction <direction>', 'in | out | both', 'both')
|
|
861
|
+
.option('--limit <n>', 'Maximum nodes and edges (default: 100)', (value) => parseInt(value, 10))
|
|
862
|
+
.option('--json', 'Output stable machine-readable JSON')
|
|
863
|
+
.action((options) => {
|
|
864
|
+
const scope = getBrainScope();
|
|
865
|
+
const graph = (0, brain_1.readRepositoryGraph)(scope.cwd);
|
|
866
|
+
if (!graph) {
|
|
867
|
+
repositoryGraphError(new Error('Repository Graph V2 is missing or corrupt. Run `neurcode brain repo-index`.'), options.json);
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
const allowedRelationships = new Set([
|
|
871
|
+
'defines', 'references', 'imports', 'exports', 'calls', 'owns',
|
|
872
|
+
'belongs_to_package', 'belongs_to_service', 'tests', 'depends_on',
|
|
873
|
+
'structurally_resembles', 'crosses_boundary',
|
|
874
|
+
]);
|
|
875
|
+
const relationship = options.relationship;
|
|
876
|
+
if (relationship && !allowedRelationships.has(relationship)) {
|
|
877
|
+
repositoryGraphError(new Error(`Unsupported relationship: ${relationship}`), options.json);
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
const direction = options.direction === 'in' || options.direction === 'out' ? options.direction : 'both';
|
|
881
|
+
const result = (0, brain_1.queryRepositoryGraph)(graph, {
|
|
882
|
+
path: options.path,
|
|
883
|
+
symbol: options.symbol,
|
|
884
|
+
relationship,
|
|
885
|
+
direction,
|
|
886
|
+
limit: options.limit,
|
|
887
|
+
});
|
|
888
|
+
const payload = { ok: true, query: options, ...result, freshness: graph.freshness };
|
|
889
|
+
if (options.json) {
|
|
890
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
console.log(chalk.bold('\nš§ Repository Graph V2 Query\n'));
|
|
894
|
+
console.log(chalk.dim(`Total matches: ${result.totalMatches} | Returned: ${result.returnedMatches} | Limit: ${result.limit} | Truncated: ${result.truncated}`));
|
|
895
|
+
console.log(chalk.dim(`Seeds: ${result.seeds.length} | Nodes: ${result.nodes.length} | Edges: ${result.edges.length}`));
|
|
896
|
+
result.edges.forEach((edge) => {
|
|
897
|
+
const posture = edge.enforcementEligible === false
|
|
898
|
+
? 'advisory/non-enforcement'
|
|
899
|
+
: 'enforcement-eligible';
|
|
900
|
+
console.log(chalk.dim(` ${edge.type}: ${edge.fromId} -> ${edge.toId} [${posture}]`));
|
|
901
|
+
});
|
|
902
|
+
});
|
|
903
|
+
brain
|
|
904
|
+
.command('repo-rebuild')
|
|
905
|
+
.description('Rebuild Repository Graph V2 from local repository state')
|
|
906
|
+
.option('--json', 'Output stable machine-readable JSON')
|
|
907
|
+
.action(async (options) => {
|
|
908
|
+
const scope = getBrainScope();
|
|
909
|
+
try {
|
|
910
|
+
const result = await (0, brain_1.indexRepositoryGraph)({ repoRoot: scope.cwd, forceRebuild: true });
|
|
911
|
+
printRepositoryGraphIndexResult(scope.cwd, result, options.json);
|
|
912
|
+
}
|
|
913
|
+
catch (error) {
|
|
914
|
+
repositoryGraphError(error, options.json);
|
|
915
|
+
}
|
|
916
|
+
});
|
|
917
|
+
brain
|
|
918
|
+
.command('repo-recover')
|
|
919
|
+
.description('Recover Repository Graph V2 from its last atomic backup or rebuild when unavailable')
|
|
920
|
+
.option('--json', 'Output stable machine-readable JSON')
|
|
921
|
+
.action(async (options) => {
|
|
922
|
+
const scope = getBrainScope();
|
|
923
|
+
try {
|
|
924
|
+
const result = await (0, brain_1.recoverRepositoryGraph)(scope.cwd);
|
|
925
|
+
printRepositoryGraphIndexResult(scope.cwd, result, options.json);
|
|
926
|
+
}
|
|
927
|
+
catch (error) {
|
|
928
|
+
repositoryGraphError(error, options.json);
|
|
929
|
+
}
|
|
930
|
+
});
|
|
931
|
+
brain
|
|
932
|
+
.command('advisory <path>')
|
|
933
|
+
.description('Run local-only advisory semantic intelligence for a proposed or current file')
|
|
934
|
+
.option('--content-file <path>', 'Local proposed content file; raw content is parsed locally and not retained')
|
|
935
|
+
.option('--path-only', 'Run without proposed content and expose coverage limitations')
|
|
936
|
+
.option('--include-suppressed', 'Include findings suppressed by local feedback')
|
|
937
|
+
.option('--no-index', 'Do not create or refresh Repository Graph V2')
|
|
938
|
+
.option('--json', 'Output stable source-free JSON')
|
|
939
|
+
.action(async (path, options) => {
|
|
940
|
+
const scope = getBrainScope();
|
|
941
|
+
try {
|
|
942
|
+
let graph = (0, brain_1.readRepositoryGraph)(scope.cwd);
|
|
943
|
+
const status = await (0, brain_1.repositoryGraphStatus)(scope.cwd);
|
|
944
|
+
if (options.index !== false && (!graph || status.state !== 'fresh')) {
|
|
945
|
+
graph = (await (0, brain_1.indexRepositoryGraph)({ repoRoot: scope.cwd })).graph;
|
|
946
|
+
}
|
|
947
|
+
else if (graph) {
|
|
948
|
+
graph = { ...graph, freshness: status };
|
|
949
|
+
}
|
|
950
|
+
if (!graph)
|
|
951
|
+
throw new Error('Repository Graph V2 is missing. Run `neurcode brain repo-index`.');
|
|
952
|
+
const target = normalizeAdvisoryTarget(scope.cwd, path);
|
|
953
|
+
let proposedSource = null;
|
|
954
|
+
let sourceKind = 'not_available';
|
|
955
|
+
if (!options.pathOnly) {
|
|
956
|
+
const contentPath = options.contentFile
|
|
957
|
+
? ((0, path_1.isAbsolute)(options.contentFile) ? options.contentFile : (0, path_1.resolve)(scope.cwd, options.contentFile))
|
|
958
|
+
: target.absolutePath;
|
|
959
|
+
if ((0, fs_1.existsSync)(contentPath)) {
|
|
960
|
+
proposedSource = (0, fs_1.readFileSync)(contentPath, 'utf8');
|
|
961
|
+
sourceKind = options.contentFile ? 'write_content' : 'post_write_disk_read';
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
const analysis = (0, proposed_change_analysis_1.analyzeProposedChange)({
|
|
965
|
+
repoRoot: scope.cwd,
|
|
966
|
+
filePath: target.relativePath,
|
|
967
|
+
proposedSource,
|
|
968
|
+
sourceKind,
|
|
969
|
+
adapterId: 'neurcode-cli',
|
|
970
|
+
timing: sourceKind === 'post_write_disk_read' ? 'after_write' : 'before_write',
|
|
971
|
+
sessionId: null,
|
|
972
|
+
planRevision: null,
|
|
973
|
+
});
|
|
974
|
+
analysis.envelope.target.operation = (0, fs_1.existsSync)(target.absolutePath) ? 'update' : 'create';
|
|
975
|
+
const result = await (0, brain_1.runSemanticAdvisory)({
|
|
976
|
+
repoRoot: scope.cwd,
|
|
977
|
+
graph,
|
|
978
|
+
change: analysis.envelope,
|
|
979
|
+
});
|
|
980
|
+
const findings = options.includeSuppressed
|
|
981
|
+
? result.findings
|
|
982
|
+
: result.findings.filter((finding) => !finding.suppressed);
|
|
983
|
+
const payload = {
|
|
984
|
+
ok: true,
|
|
985
|
+
truth: 'advisory',
|
|
986
|
+
blocking: false,
|
|
987
|
+
cacheHit: result.cacheHit,
|
|
988
|
+
cacheKey: result.cacheKey,
|
|
989
|
+
graph: {
|
|
990
|
+
graphId: graph.graphId,
|
|
991
|
+
generation: graph.generation,
|
|
992
|
+
freshness: graph.freshness,
|
|
993
|
+
},
|
|
994
|
+
host: analysis.envelope.host,
|
|
995
|
+
findings,
|
|
996
|
+
suppressedCount: result.findings.filter((finding) => finding.suppressed).length,
|
|
997
|
+
privacy: analysis.envelope.privacy,
|
|
998
|
+
};
|
|
999
|
+
if (options.json) {
|
|
1000
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
console.log(chalk.bold('\nš§ Advisory Semantic Intelligence V2\n'));
|
|
1004
|
+
console.log(chalk.dim('Truth: advisory Ā· Blocking: never'));
|
|
1005
|
+
console.log(chalk.dim(`Graph: ${graph.graphId} generation ${graph.generation} (${graph.freshness.state})`));
|
|
1006
|
+
console.log(chalk.dim(`Cache: ${result.cacheHit ? 'hit' : 'miss'}`));
|
|
1007
|
+
if (findings.length === 0) {
|
|
1008
|
+
console.log(chalk.dim('No unsuppressed advisory findings.'));
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
findings.forEach((finding, index) => {
|
|
1012
|
+
console.log(chalk.white(` ${index + 1}. ${finding.category} Ā· ${(finding.confidence * 100).toFixed(0)}%`));
|
|
1013
|
+
console.log(chalk.dim(` ${finding.rationaleCategories.join(', ')}`));
|
|
1014
|
+
console.log(chalk.dim(` ${finding.related.map((item) => item.path || item.symbol || item.hash).filter(Boolean).join(', ')}`));
|
|
1015
|
+
console.log(chalk.dim(` limitations: ${finding.limitations.join(' ')}`));
|
|
1016
|
+
console.log(chalk.dim(` id: ${finding.findingId}`));
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
catch (error) {
|
|
1020
|
+
repositoryGraphError(error, options.json);
|
|
1021
|
+
}
|
|
1022
|
+
});
|
|
1023
|
+
brain
|
|
1024
|
+
.command('advisory-suppress <key>')
|
|
1025
|
+
.description('Suppress a finding ID, category:<name>, path:<path>, or fingerprint:<hash> locally')
|
|
1026
|
+
.option('--remove', 'Remove the suppression instead')
|
|
1027
|
+
.option('--json', 'Output stable source-free JSON')
|
|
1028
|
+
.action((key, options) => {
|
|
1029
|
+
const scope = getBrainScope();
|
|
1030
|
+
try {
|
|
1031
|
+
const registry = new brain_1.SemanticAdvisoryRegistry(scope.cwd);
|
|
1032
|
+
const status = options.remove ? registry.unsuppress(key) : registry.suppress(key);
|
|
1033
|
+
if (options.json)
|
|
1034
|
+
console.log(JSON.stringify({ ok: true, status }, null, 2));
|
|
1035
|
+
else
|
|
1036
|
+
console.log(chalk.green(`\nā
Advisory suppression ${options.remove ? 'removed' : 'saved'}: ${key}\n`));
|
|
1037
|
+
}
|
|
1038
|
+
catch (error) {
|
|
1039
|
+
repositoryGraphError(error, options.json);
|
|
1040
|
+
}
|
|
1041
|
+
});
|
|
1042
|
+
brain
|
|
1043
|
+
.command('advisory-feedback <finding-id>')
|
|
1044
|
+
.description('Record source-free advisory feedback: accepted, dismissed, duplicate, or useful')
|
|
1045
|
+
.requiredOption('--category <category>', 'Advisory category')
|
|
1046
|
+
.requiredOption('--outcome <outcome>', 'accepted | dismissed | duplicate | useful')
|
|
1047
|
+
.option('--json', 'Output stable source-free JSON')
|
|
1048
|
+
.action((findingId, options) => {
|
|
1049
|
+
const scope = getBrainScope();
|
|
1050
|
+
try {
|
|
1051
|
+
const outcomes = ['accepted', 'dismissed', 'duplicate', 'useful'];
|
|
1052
|
+
if (!outcomes.includes(options.outcome)) {
|
|
1053
|
+
throw new Error(`Unsupported advisory feedback outcome: ${options.outcome}`);
|
|
1054
|
+
}
|
|
1055
|
+
const registry = new brain_1.SemanticAdvisoryRegistry(scope.cwd);
|
|
1056
|
+
const status = registry.recordFeedback({
|
|
1057
|
+
findingId,
|
|
1058
|
+
category: advisoryCategory(options.category),
|
|
1059
|
+
outcome: options.outcome,
|
|
1060
|
+
});
|
|
1061
|
+
if (options.json)
|
|
1062
|
+
console.log(JSON.stringify({ ok: true, status }, null, 2));
|
|
1063
|
+
else
|
|
1064
|
+
console.log(chalk.green(`\nā
Advisory feedback recorded: ${options.outcome}\n`));
|
|
1065
|
+
}
|
|
1066
|
+
catch (error) {
|
|
1067
|
+
repositoryGraphError(error, options.json);
|
|
1068
|
+
}
|
|
1069
|
+
});
|
|
1070
|
+
brain
|
|
1071
|
+
.command('advisory-status')
|
|
1072
|
+
.description('Show local advisory cache, suppressions, feedback, and privacy mode')
|
|
1073
|
+
.option('--json', 'Output stable source-free JSON')
|
|
1074
|
+
.action((options) => {
|
|
1075
|
+
const scope = getBrainScope();
|
|
1076
|
+
const status = new brain_1.SemanticAdvisoryRegistry(scope.cwd).status();
|
|
1077
|
+
if (options.json) {
|
|
1078
|
+
console.log(JSON.stringify({ ok: true, status }, null, 2));
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
console.log(chalk.bold('\nš§ Advisory Semantic Intelligence Status\n'));
|
|
1082
|
+
console.log(chalk.dim(`Registry: ${status.path}`));
|
|
1083
|
+
console.log(chalk.dim(`Cache entries: ${status.cacheEntries}`));
|
|
1084
|
+
console.log(chalk.dim(`Suppressions: ${status.suppressions.length}`));
|
|
1085
|
+
console.log(chalk.dim(`Feedback IDs: ${Object.keys(status.feedback).length}`));
|
|
1086
|
+
console.log(chalk.dim('Privacy: local-only; no source, diff, prompt, or chat upload.'));
|
|
1087
|
+
});
|
|
598
1088
|
// -- brain inspect ----------------------------------------------------------
|
|
599
1089
|
brain
|
|
600
1090
|
.command('inspect [query...]')
|
|
@@ -608,6 +1098,11 @@ function brainCommand(program) {
|
|
|
608
1098
|
const limit = Number.isFinite(options.limit) ? Math.max(1, Math.min(50, options.limit)) : 12;
|
|
609
1099
|
let artifact = options.rebuild ? null : (0, local_repo_brain_1.readLocalRepoBrain)(scope.cwd);
|
|
610
1100
|
let rebuilt = false;
|
|
1101
|
+
let canonicalGraph = (0, brain_1.readRepositoryGraph)(scope.cwd);
|
|
1102
|
+
if (!canonicalGraph || options.rebuild) {
|
|
1103
|
+
canonicalGraph = (await (0, brain_1.indexRepositoryGraph)({ repoRoot: scope.cwd, forceRebuild: options.rebuild === true })).graph;
|
|
1104
|
+
}
|
|
1105
|
+
const canonicalFreshness = await (0, brain_1.repositoryGraphStatus)(scope.cwd);
|
|
611
1106
|
if (!artifact) {
|
|
612
1107
|
artifact = (0, local_repo_brain_1.buildLocalRepoBrain)(scope.cwd, { experimentalFingerprintReuse: options.experimentalFingerprintReuse });
|
|
613
1108
|
(0, local_repo_brain_1.writeLocalRepoBrain)(scope.cwd, artifact);
|
|
@@ -617,6 +1112,17 @@ function brainCommand(program) {
|
|
|
617
1112
|
const results = query ? (0, local_repo_brain_1.searchLocalRepoBrain)(artifact, query, limit) : [];
|
|
618
1113
|
const payload = {
|
|
619
1114
|
repoRoot: scope.cwd,
|
|
1115
|
+
repositoryIntelligenceModel: {
|
|
1116
|
+
canonicalLifecycle: 'repository_graph_v2',
|
|
1117
|
+
surface: 'legacy_brain_compatibility_projection',
|
|
1118
|
+
compatibilityOnly: true,
|
|
1119
|
+
},
|
|
1120
|
+
canonicalGraph: {
|
|
1121
|
+
graphId: canonicalGraph.graphId,
|
|
1122
|
+
schemaVersion: canonicalGraph.schemaVersion,
|
|
1123
|
+
freshness: canonicalFreshness,
|
|
1124
|
+
coverage: canonicalGraph.coverage,
|
|
1125
|
+
},
|
|
620
1126
|
jsonPath: (0, local_repo_brain_1.localRepoBrainPath)(scope.cwd),
|
|
621
1127
|
markdownPath: (0, local_repo_brain_1.localRepoBrainMarkdownPath)(scope.cwd),
|
|
622
1128
|
rebuilt,
|
|
@@ -638,6 +1144,7 @@ function brainCommand(program) {
|
|
|
638
1144
|
console.log(chalk.dim(`Repo Root: ${scope.cwd}`));
|
|
639
1145
|
console.log(chalk.dim(`Artifact: ${(0, local_repo_brain_1.localRepoBrainPath)(scope.cwd)}`));
|
|
640
1146
|
console.log(chalk.dim(`Artifact Hash: ${artifact.artifactHash}`));
|
|
1147
|
+
console.log(chalk.yellow('Compatibility projection: use `brain repo-status` for canonical freshness and completeness.'));
|
|
641
1148
|
if (rebuilt) {
|
|
642
1149
|
(0, messages_1.printInfo)('Brain index created', 'No existing local repo brain was found, so Neurcode built one first.');
|
|
643
1150
|
}
|
|
@@ -714,13 +1221,34 @@ function brainCommand(program) {
|
|
|
714
1221
|
process.exit(1);
|
|
715
1222
|
}
|
|
716
1223
|
if (options.rebuild) {
|
|
1224
|
+
await (0, brain_1.indexRepositoryGraph)({ repoRoot: scope.cwd, forceRebuild: true });
|
|
717
1225
|
const rebuilt = (0, local_repo_brain_1.buildLocalRepoBrain)(scope.cwd);
|
|
718
1226
|
(0, local_repo_brain_1.writeLocalRepoBrain)(scope.cwd, rebuilt);
|
|
719
1227
|
}
|
|
1228
|
+
else if (options.index !== false && !(0, brain_1.readRepositoryGraph)(scope.cwd)) {
|
|
1229
|
+
await (0, brain_1.indexRepositoryGraph)({ repoRoot: scope.cwd });
|
|
1230
|
+
}
|
|
720
1231
|
const report = (0, repo_brain_impact_1.buildRepoBrainImpactForRepo)(scope.cwd, paths, { autoBuild: options.index !== false });
|
|
721
1232
|
const summary = (0, repo_brain_impact_1.summarizeImpact)(report);
|
|
1233
|
+
const canonicalGraph = (0, brain_1.readRepositoryGraph)(scope.cwd);
|
|
1234
|
+
const canonicalFreshness = canonicalGraph ? await (0, brain_1.repositoryGraphStatus)(scope.cwd) : null;
|
|
722
1235
|
if (options.json) {
|
|
723
|
-
console.log(JSON.stringify({
|
|
1236
|
+
console.log(JSON.stringify({
|
|
1237
|
+
repoRoot: scope.cwd,
|
|
1238
|
+
repositoryIntelligenceModel: {
|
|
1239
|
+
canonicalLifecycle: 'repository_graph_v2',
|
|
1240
|
+
surface: 'legacy_impact_compatibility_projection',
|
|
1241
|
+
compatibilityOnly: true,
|
|
1242
|
+
},
|
|
1243
|
+
canonicalGraph: canonicalGraph ? {
|
|
1244
|
+
graphId: canonicalGraph.graphId,
|
|
1245
|
+
schemaVersion: canonicalGraph.schemaVersion,
|
|
1246
|
+
freshness: canonicalFreshness,
|
|
1247
|
+
coverage: canonicalGraph.coverage,
|
|
1248
|
+
} : null,
|
|
1249
|
+
report,
|
|
1250
|
+
summary,
|
|
1251
|
+
}, null, 2));
|
|
724
1252
|
return;
|
|
725
1253
|
}
|
|
726
1254
|
if (options.summary) {
|
|
@@ -747,6 +1275,7 @@ function brainCommand(program) {
|
|
|
747
1275
|
(0, messages_1.printInfo)('Brain index created', 'No existing local repo brain was found, so Neurcode built one first.');
|
|
748
1276
|
}
|
|
749
1277
|
console.log(chalk.dim(`Repo Root: ${scope.cwd}\n`));
|
|
1278
|
+
console.log(chalk.yellow('Impact is a compatibility projection; Repository Graph V2 owns canonical freshness/completeness.'));
|
|
750
1279
|
console.log((0, repo_brain_impact_1.renderRepoBrainImpactText)(report));
|
|
751
1280
|
(0, messages_1.printInfo)('Labels', 'Deterministic = compiled path/CODEOWNERS/import-graph facts. Advisory = heuristic reuse/proximity/reviewer guidance.');
|
|
752
1281
|
});
|
|
@@ -1111,7 +1640,7 @@ function brainCommand(program) {
|
|
|
1111
1640
|
updatedAt: entry.updatedAt,
|
|
1112
1641
|
lastSeenAt: entry.lastSeenAt,
|
|
1113
1642
|
}))
|
|
1114
|
-
.filter((entry) => Boolean(entry.path));
|
|
1643
|
+
.filter((entry) => Boolean(entry.path) && (0, team_memory_path_hygiene_1.isTeamMemoryProjectPath)(entry.path));
|
|
1115
1644
|
const scopedFiles = scopedFilesRaw.length > 0
|
|
1116
1645
|
? scopedFilesRaw
|
|
1117
1646
|
: scanFiles(scope.cwd, scope.cwd, 300).map((path) => ({
|
|
@@ -1161,7 +1690,9 @@ function brainCommand(program) {
|
|
|
1161
1690
|
const effectiveAuthorTouches = focusAuthorTouches.size > 0 ? focusAuthorTouches : authorTouches;
|
|
1162
1691
|
const topContributors = rankTopEntries(effectiveAuthorTouches.entries(), ([, count]) => count, limit)
|
|
1163
1692
|
.map(([author, touches]) => ({ author, touches }));
|
|
1164
|
-
const events = Array.isArray(scopeStore.events)
|
|
1693
|
+
const events = Array.isArray(scopeStore.events)
|
|
1694
|
+
? scopeStore.events.filter((event) => !event.filePath || (0, team_memory_path_hygiene_1.isTeamMemoryProjectPath)(event.filePath))
|
|
1695
|
+
: [];
|
|
1165
1696
|
const recentDecisions = [...events]
|
|
1166
1697
|
.sort((a, b) => sortIsoDesc(a.timestamp, b.timestamp))
|
|
1167
1698
|
.slice(0, Math.max(limit, 12))
|