@papyruslabsai/seshat-mcp 0.8.0 → 0.10.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/dist/index.js +48 -18
- package/dist/loader.d.ts +5 -0
- package/dist/loader.js +22 -0
- package/dist/tools/diff.js +11 -11
- package/dist/tools/functors.d.ts +7 -0
- package/dist/tools/functors.js +56 -19
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -34,7 +34,7 @@ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextpro
|
|
|
34
34
|
import { MultiLoader } from './loader.js';
|
|
35
35
|
import { bootstrap } from './bootstrap.js';
|
|
36
36
|
import { initTools, queryEntities, getEntity, getDependencies, getDataFlow, findByConstraint, getBlastRadius, listModules, getTopology, } from './tools/index.js';
|
|
37
|
-
import { findDeadCode, findLayerViolations, getCouplingMetrics, getAuthMatrix, findErrorGaps, getTestCoverage, getOptimalContext, estimateTaskCost, reportActualBurn, find_runtime_violations, find_ownership_violations, query_traits, simulate_mutation, query_data_targets, find_exposure_leaks, find_semantic_clones, } from './tools/functors.js';
|
|
37
|
+
import { findDeadCode, findLayerViolations, getCouplingMetrics, getAuthMatrix, findErrorGaps, getTestCoverage, getOptimalContext, estimateTaskCost, reportActualBurn, find_runtime_violations, find_ownership_violations, query_traits, simulate_mutation, query_data_targets, find_exposure_leaks, find_semantic_clones, create_symbol, } from './tools/functors.js';
|
|
38
38
|
import { diffBundle, conflictMatrix, } from './tools/diff.js';
|
|
39
39
|
// ─── Project Discovery ───────────────────────────────────────────
|
|
40
40
|
/**
|
|
@@ -388,10 +388,10 @@ const TOOLS = [
|
|
|
388
388
|
},
|
|
389
389
|
},
|
|
390
390
|
},
|
|
391
|
-
// ───
|
|
391
|
+
// ─── Analysis & Impact Tools ───────────────────────────────────
|
|
392
392
|
{
|
|
393
393
|
name: 'find_runtime_violations',
|
|
394
|
-
description: 'Analyze the call graph
|
|
394
|
+
description: 'Analyze the call graph for runtime environment leaks. Finds architectural boundary issues where framework-agnostic code improperly imports framework-specific code (e.g. pure logic calling React hooks) or where incompatible frameworks mix directly.',
|
|
395
395
|
inputSchema: {
|
|
396
396
|
type: 'object',
|
|
397
397
|
properties: {
|
|
@@ -401,7 +401,7 @@ const TOOLS = [
|
|
|
401
401
|
},
|
|
402
402
|
{
|
|
403
403
|
name: 'find_ownership_violations',
|
|
404
|
-
description: 'Analyze the codebase
|
|
404
|
+
description: 'Analyze the codebase for memory and lifecycle constraints. Flags entities with complex ownership rules, unsafe blocks, escaping boundaries, or illegal mutability patterns on borrowed references.',
|
|
405
405
|
inputSchema: {
|
|
406
406
|
type: 'object',
|
|
407
407
|
properties: {
|
|
@@ -411,7 +411,7 @@ const TOOLS = [
|
|
|
411
411
|
},
|
|
412
412
|
{
|
|
413
413
|
name: 'query_traits',
|
|
414
|
-
description: 'Search the codebase
|
|
414
|
+
description: 'Search the codebase for specific functional capabilities. Allows you to find entities by their abstract traits (e.g., "fallible", "asyncContext", "generator") regardless of their structural syntax.',
|
|
415
415
|
inputSchema: {
|
|
416
416
|
type: 'object',
|
|
417
417
|
properties: {
|
|
@@ -426,20 +426,20 @@ const TOOLS = [
|
|
|
426
426
|
},
|
|
427
427
|
{
|
|
428
428
|
name: 'simulate_mutation',
|
|
429
|
-
description: 'The
|
|
429
|
+
description: 'The Impact Simulator. Proposes a hypothetical change to an entity (like adding a "fallible" or "auth" requirement) and simulates the architectural fallout upstream and downstream. Returns a blueprint of exactly which other entities will break and what fixes they require.',
|
|
430
430
|
inputSchema: {
|
|
431
431
|
type: 'object',
|
|
432
432
|
properties: {
|
|
433
433
|
project: projectParam,
|
|
434
434
|
entity_id: {
|
|
435
435
|
type: 'string',
|
|
436
|
-
description: 'The target entity to
|
|
436
|
+
description: 'The target entity to analyze.',
|
|
437
437
|
},
|
|
438
438
|
mutation: {
|
|
439
439
|
type: 'object',
|
|
440
440
|
description: 'The hypothetical change to apply.',
|
|
441
441
|
properties: {
|
|
442
|
-
dimension: { type: 'string', enum: ['constraints', 'traits'] },
|
|
442
|
+
dimension: { type: 'string', enum: ['constraints', 'traits'], description: 'The attribute scope to mutate.' },
|
|
443
443
|
change: {
|
|
444
444
|
type: 'object',
|
|
445
445
|
properties: {
|
|
@@ -456,7 +456,7 @@ const TOOLS = [
|
|
|
456
456
|
},
|
|
457
457
|
{
|
|
458
458
|
name: 'query_data_targets',
|
|
459
|
-
description: 'Search the codebase
|
|
459
|
+
description: 'Search the codebase for data interactions to find all entities that read or write to a specific database table, state object, or data source. This acts as a reverse-index for data flow, essential for planning migrations or state refactors.',
|
|
460
460
|
inputSchema: {
|
|
461
461
|
type: 'object',
|
|
462
462
|
properties: {
|
|
@@ -471,7 +471,7 @@ const TOOLS = [
|
|
|
471
471
|
},
|
|
472
472
|
{
|
|
473
473
|
name: 'find_exposure_leaks',
|
|
474
|
-
description: 'Analyze the call graph
|
|
474
|
+
description: 'Analyze the call graph for architectural visibility leaks. Flags paths where a "public" or "api" entity directly accesses a "private" internal entity, potentially leaking sensitive data or bypassing internal service boundaries.',
|
|
475
475
|
inputSchema: {
|
|
476
476
|
type: 'object',
|
|
477
477
|
properties: {
|
|
@@ -481,7 +481,7 @@ const TOOLS = [
|
|
|
481
481
|
},
|
|
482
482
|
{
|
|
483
483
|
name: 'find_semantic_clones',
|
|
484
|
-
description: 'Analyze the codebase
|
|
484
|
+
description: 'Analyze the codebase for duplicated logic blocks. Normalizes variables and compares code structure to identify identical algorithms written across different files or even different languages.',
|
|
485
485
|
inputSchema: {
|
|
486
486
|
type: 'object',
|
|
487
487
|
properties: {
|
|
@@ -493,10 +493,37 @@ const TOOLS = [
|
|
|
493
493
|
},
|
|
494
494
|
},
|
|
495
495
|
},
|
|
496
|
-
|
|
496
|
+
{
|
|
497
|
+
name: 'create_symbol',
|
|
498
|
+
description: 'Register a new code symbol in the architectural graph. This does not write to disk, but allows the Impact Simulator and Conflict Matrix to reason about a symbol that is pending creation. Essential for planning new features that introduce new files or modules.',
|
|
499
|
+
inputSchema: {
|
|
500
|
+
type: 'object',
|
|
501
|
+
properties: {
|
|
502
|
+
project: projectParam,
|
|
503
|
+
id: {
|
|
504
|
+
type: 'string',
|
|
505
|
+
description: 'The unique ID or name for the new symbol.',
|
|
506
|
+
},
|
|
507
|
+
source_file: {
|
|
508
|
+
type: 'string',
|
|
509
|
+
description: 'The relative path to the file where this symbol will be created.',
|
|
510
|
+
},
|
|
511
|
+
layer: {
|
|
512
|
+
type: 'string',
|
|
513
|
+
description: 'The architectural layer (e.g., "service", "route", "component").',
|
|
514
|
+
},
|
|
515
|
+
description: {
|
|
516
|
+
type: 'string',
|
|
517
|
+
description: 'Brief description of the symbol\'s purpose.',
|
|
518
|
+
},
|
|
519
|
+
},
|
|
520
|
+
required: ['id', 'source_file'],
|
|
521
|
+
},
|
|
522
|
+
},
|
|
523
|
+
// ─── Diff Tools ─────────────────────────────────────────────────
|
|
497
524
|
{
|
|
498
525
|
name: 'diff_bundle',
|
|
499
|
-
description: 'Compare
|
|
526
|
+
description: 'Compare code structure between a worktree and the loaded project. Shows which symbols were added, removed, or modified — not a line diff, but a structural diff showing changed signatures, dependencies, and implementation logic.',
|
|
500
527
|
inputSchema: {
|
|
501
528
|
type: 'object',
|
|
502
529
|
properties: {
|
|
@@ -515,7 +542,7 @@ const TOOLS = [
|
|
|
515
542
|
},
|
|
516
543
|
{
|
|
517
544
|
name: 'conflict_matrix',
|
|
518
|
-
description: 'Given multiple tasks, classify every task pair into conflict tiers
|
|
545
|
+
description: 'Given multiple tasks, classify every task pair into conflict tiers to determine parallelization safety. Tier 1 (different files, safe), Tier 2 (same file, different symbols, safe), Tier 3 (same symbol, orthogonal changes like imports vs logic, risky but parallelizable), Tier 4 (same symbol, overlapping changes, MUST sequence). Passing "scopes" per task enables Tier 3 downgrades.',
|
|
519
546
|
inputSchema: {
|
|
520
547
|
type: 'object',
|
|
521
548
|
properties: {
|
|
@@ -532,16 +559,16 @@ const TOOLS = [
|
|
|
532
559
|
entity_ids: {
|
|
533
560
|
type: 'array',
|
|
534
561
|
items: { type: 'string' },
|
|
535
|
-
description: '
|
|
562
|
+
description: 'Symbol names or IDs that this task will modify',
|
|
536
563
|
},
|
|
537
564
|
dimensions: {
|
|
538
565
|
type: 'array',
|
|
539
566
|
items: { type: 'string' },
|
|
540
|
-
description: 'Optional:
|
|
567
|
+
description: 'Optional: Code scopes this task will modify (e.g., "edges", "struct", "semantics", "constraints"). Used to downgrade conflicts via spatial separation.',
|
|
541
568
|
},
|
|
542
569
|
expand_blast_radius: {
|
|
543
570
|
type: 'boolean',
|
|
544
|
-
description: 'Include transitively affected
|
|
571
|
+
description: 'Include transitively affected symbols in the conflict check (default: false)',
|
|
545
572
|
},
|
|
546
573
|
},
|
|
547
574
|
required: ['id', 'entity_ids'],
|
|
@@ -697,7 +724,7 @@ async function main() {
|
|
|
697
724
|
case 'report_actual_burn':
|
|
698
725
|
result = await reportActualBurn(args);
|
|
699
726
|
break;
|
|
700
|
-
//
|
|
727
|
+
// Analysis & Impact Tools
|
|
701
728
|
case 'find_runtime_violations':
|
|
702
729
|
result = find_runtime_violations(args);
|
|
703
730
|
break;
|
|
@@ -719,6 +746,9 @@ async function main() {
|
|
|
719
746
|
case 'find_semantic_clones':
|
|
720
747
|
result = find_semantic_clones(args);
|
|
721
748
|
break;
|
|
749
|
+
case 'create_symbol':
|
|
750
|
+
result = create_symbol(args);
|
|
751
|
+
break;
|
|
722
752
|
// Diff Tools
|
|
723
753
|
case 'diff_bundle':
|
|
724
754
|
result = await diffBundle(args);
|
package/dist/loader.d.ts
CHANGED
|
@@ -46,5 +46,10 @@ export declare class MultiLoader {
|
|
|
46
46
|
isMultiProject(): boolean;
|
|
47
47
|
isLoaded(): boolean;
|
|
48
48
|
hasProject(name: string): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Register a virtual entity in memory.
|
|
51
|
+
* Used for Phase 1/2 of the Harness to reason about entities that haven't been written to disk yet.
|
|
52
|
+
*/
|
|
53
|
+
registerVirtualEntity(entity: JstfEntity, project?: string): void;
|
|
49
54
|
totalEntities(): number;
|
|
50
55
|
}
|
package/dist/loader.js
CHANGED
|
@@ -190,6 +190,28 @@ export class MultiLoader {
|
|
|
190
190
|
hasProject(name) {
|
|
191
191
|
return this.projects.has(name);
|
|
192
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* Register a virtual entity in memory.
|
|
195
|
+
* Used for Phase 1/2 of the Harness to reason about entities that haven't been written to disk yet.
|
|
196
|
+
*/
|
|
197
|
+
registerVirtualEntity(entity, project) {
|
|
198
|
+
if (!this.loaded)
|
|
199
|
+
this.load();
|
|
200
|
+
const p = this.resolveProject(project);
|
|
201
|
+
if (!p)
|
|
202
|
+
return;
|
|
203
|
+
const entities = this.projectEntities.get(p) || [];
|
|
204
|
+
entity._project = p;
|
|
205
|
+
// If entity already exists, update it, otherwise append
|
|
206
|
+
const existingIdx = entities.findIndex(e => e.id === entity.id);
|
|
207
|
+
if (existingIdx !== -1) {
|
|
208
|
+
entities[existingIdx] = { ...entities[existingIdx], ...entity };
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
entities.push(entity);
|
|
212
|
+
}
|
|
213
|
+
this.projectEntities.set(p, entities);
|
|
214
|
+
}
|
|
193
215
|
totalEntities() {
|
|
194
216
|
let total = 0;
|
|
195
217
|
for (const entities of this.projectEntities.values()) {
|
package/dist/tools/diff.js
CHANGED
|
@@ -262,13 +262,13 @@ export async function diffBundle(args) {
|
|
|
262
262
|
return result;
|
|
263
263
|
}
|
|
264
264
|
// ─── Conflict Tier Classification ─────────────────────────────────
|
|
265
|
-
const ZONE_1 = new Set(['edges', 'imports'
|
|
266
|
-
const ZONE_2 = new Set(['struct', '
|
|
267
|
-
const ZONE_3 = new Set(['semantics', '
|
|
268
|
-
function getZones(
|
|
265
|
+
const ZONE_1 = new Set(['edges', 'imports']); // Header / Dependencies
|
|
266
|
+
const ZONE_2 = new Set(['struct', 'constraints', 'traits', 'ownership']); // Signature / Modifiers
|
|
267
|
+
const ZONE_3 = new Set(['semantics', 'data', 'runtime']); // Logic / Implementation
|
|
268
|
+
function getZones(scopes) {
|
|
269
269
|
const zones = new Set();
|
|
270
|
-
for (const
|
|
271
|
-
const lower =
|
|
270
|
+
for (const s of scopes) {
|
|
271
|
+
const lower = s.toLowerCase();
|
|
272
272
|
if (ZONE_1.has(lower))
|
|
273
273
|
zones.add(1);
|
|
274
274
|
if (ZONE_2.has(lower))
|
|
@@ -279,7 +279,7 @@ function getZones(dimensions) {
|
|
|
279
279
|
return zones;
|
|
280
280
|
}
|
|
281
281
|
function classifyConflictTier(taskA, taskB) {
|
|
282
|
-
// Check
|
|
282
|
+
// Check symbol overlap
|
|
283
283
|
const sharedEntities = [];
|
|
284
284
|
for (const id of taskA.entityIds) {
|
|
285
285
|
if (taskB.entityIds.has(id)) {
|
|
@@ -287,7 +287,7 @@ function classifyConflictTier(taskA, taskB) {
|
|
|
287
287
|
}
|
|
288
288
|
}
|
|
289
289
|
if (sharedEntities.length > 0) {
|
|
290
|
-
// Both touch same
|
|
290
|
+
// Both touch same symbol. Check scopes to see if we can downgrade from Tier 4 (Sequential) to Tier 3 (Parallelizable Orthogonal)
|
|
291
291
|
if (taskA.dimensions.size > 0 && taskB.dimensions.size > 0) {
|
|
292
292
|
const zonesA = getZones(taskA.dimensions);
|
|
293
293
|
const zonesB = getZones(taskB.dimensions);
|
|
@@ -299,7 +299,7 @@ function classifyConflictTier(taskA, taskB) {
|
|
|
299
299
|
if (!spatialCollision) {
|
|
300
300
|
return {
|
|
301
301
|
tier: 3,
|
|
302
|
-
reason: `${sharedEntities.length} shared
|
|
302
|
+
reason: `${sharedEntities.length} shared symbols, but orthogonal change scopes (Tier 3) — safe to parallelize`,
|
|
303
303
|
sharedFiles: [],
|
|
304
304
|
sharedEntities,
|
|
305
305
|
};
|
|
@@ -307,7 +307,7 @@ function classifyConflictTier(taskA, taskB) {
|
|
|
307
307
|
}
|
|
308
308
|
return {
|
|
309
309
|
tier: 4,
|
|
310
|
-
reason: `${sharedEntities.length} shared
|
|
310
|
+
reason: `${sharedEntities.length} shared symbols in same/unknown change scope (Tier 4) — MUST sequence to prevent merge conflict`,
|
|
311
311
|
sharedFiles: [],
|
|
312
312
|
sharedEntities,
|
|
313
313
|
};
|
|
@@ -329,7 +329,7 @@ function classifyConflictTier(taskA, taskB) {
|
|
|
329
329
|
}
|
|
330
330
|
return {
|
|
331
331
|
tier: 2,
|
|
332
|
-
reason: `${sharedFiles.length} shared files but different
|
|
332
|
+
reason: `${sharedFiles.length} shared files but different symbols — safe to parallelize`,
|
|
333
333
|
sharedFiles,
|
|
334
334
|
sharedEntities: [],
|
|
335
335
|
};
|
package/dist/tools/functors.d.ts
CHANGED
|
@@ -98,3 +98,10 @@ export declare function find_semantic_clones(args?: {
|
|
|
98
98
|
project?: string;
|
|
99
99
|
min_complexity?: number;
|
|
100
100
|
}): unknown;
|
|
101
|
+
export declare function create_symbol(args: {
|
|
102
|
+
id: string;
|
|
103
|
+
source_file: string;
|
|
104
|
+
layer?: string;
|
|
105
|
+
project?: string;
|
|
106
|
+
description?: string;
|
|
107
|
+
}): unknown;
|
package/dist/tools/functors.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Each tool computes derived insights (dead code, layer violations,
|
|
6
6
|
* coupling metrics, auth coverage, etc.) from the entity data.
|
|
7
7
|
*/
|
|
8
|
+
import path from 'path';
|
|
8
9
|
import { computeBlastRadius } from '../graph.js';
|
|
9
10
|
import { getLoader, getGraph, validateProject, entityName, entityLayer, entitySummary, } from './index.js';
|
|
10
11
|
import { isSupabaseConfigured, insertPrediction, updateActualBurn, abandonPrediction, listPredictions, } from '../supabase.js';
|
|
@@ -866,7 +867,7 @@ export async function reportActualBurn(args) {
|
|
|
866
867
|
_summary: `Prediction ${updated.id.slice(0, 8)}… closed. Predicted ${updated.predicted_total} tokens, actual ${updated.actual_total_tokens} tokens. Drift: ${updated.drift_ratio != null ? `${(updated.drift_ratio * 100).toFixed(1)}%` : 'N/A'}.`,
|
|
867
868
|
};
|
|
868
869
|
}
|
|
869
|
-
// ─── Tool: find_runtime_violations (
|
|
870
|
+
// ─── Tool: find_runtime_violations (Runtime Context) ────────────
|
|
870
871
|
export function find_runtime_violations(args) {
|
|
871
872
|
const projErr = validateProject(args?.project);
|
|
872
873
|
if (projErr)
|
|
@@ -903,11 +904,11 @@ export function find_runtime_violations(args) {
|
|
|
903
904
|
}
|
|
904
905
|
return {
|
|
905
906
|
totalViolations: violations.length,
|
|
906
|
-
_summary: `Found ${violations.length} runtime/framework boundary violations
|
|
907
|
+
_summary: `Found ${violations.length} runtime/framework boundary violations`,
|
|
907
908
|
violations: violations.slice(0, 100),
|
|
908
909
|
};
|
|
909
910
|
}
|
|
910
|
-
// ─── Tool: find_ownership_violations (
|
|
911
|
+
// ─── Tool: find_ownership_violations (Ownership & Lifetimes) ─────
|
|
911
912
|
export function find_ownership_violations(args) {
|
|
912
913
|
const projErr = validateProject(args?.project);
|
|
913
914
|
if (projErr)
|
|
@@ -944,11 +945,11 @@ export function find_ownership_violations(args) {
|
|
|
944
945
|
}
|
|
945
946
|
return {
|
|
946
947
|
totalViolations: violations.length,
|
|
947
|
-
_summary: `Found ${violations.length}
|
|
948
|
+
_summary: `Found ${violations.length} symbols with strict/violating ownership constraints`,
|
|
948
949
|
violations: violations.slice(0, 100),
|
|
949
950
|
};
|
|
950
951
|
}
|
|
951
|
-
// ─── Tool: query_traits (
|
|
952
|
+
// ─── Tool: query_traits (Trait Search) ───────────────────────────
|
|
952
953
|
export function query_traits(args) {
|
|
953
954
|
const projErr = validateProject(args.project);
|
|
954
955
|
if (projErr)
|
|
@@ -988,11 +989,11 @@ export function query_traits(args) {
|
|
|
988
989
|
return {
|
|
989
990
|
trait: args.trait,
|
|
990
991
|
total: results.length,
|
|
991
|
-
_summary: `Found ${results.length}
|
|
992
|
+
_summary: `Found ${results.length} symbols implementing the '${args.trait}' trait`,
|
|
992
993
|
entities: results.slice(0, 100).map(entitySummary),
|
|
993
994
|
};
|
|
994
995
|
}
|
|
995
|
-
// ─── Tool: simulate_mutation (
|
|
996
|
+
// ─── Tool: simulate_mutation (Impact Simulator) ──────────────────
|
|
996
997
|
export function simulate_mutation(args) {
|
|
997
998
|
const projErr = validateProject(args.project);
|
|
998
999
|
if (projErr)
|
|
@@ -1002,7 +1003,7 @@ export function simulate_mutation(args) {
|
|
|
1002
1003
|
const targetEntity = loader.getEntityById(args.entity_id, args.project)
|
|
1003
1004
|
|| loader.getEntityByName(args.entity_id, args.project);
|
|
1004
1005
|
if (!targetEntity) {
|
|
1005
|
-
return { error: `
|
|
1006
|
+
return { error: `Symbol not found: ${args.entity_id}` };
|
|
1006
1007
|
}
|
|
1007
1008
|
const targetId = targetEntity.id;
|
|
1008
1009
|
const { dimension, change } = args.mutation;
|
|
@@ -1037,7 +1038,7 @@ export function simulate_mutation(args) {
|
|
|
1037
1038
|
fallout.push({
|
|
1038
1039
|
entity: entitySummary(callerEntity),
|
|
1039
1040
|
distance: depth + 1,
|
|
1040
|
-
reason: `Calls a newly fallible
|
|
1041
|
+
reason: `Calls a newly fallible path but lacks error handling.`,
|
|
1041
1042
|
requiredFix: `Wrap call to ${currentId} in try/catch or propagate error.`,
|
|
1042
1043
|
});
|
|
1043
1044
|
// Error propagates up if not caught, so we continue queueing
|
|
@@ -1068,12 +1069,12 @@ export function simulate_mutation(args) {
|
|
|
1068
1069
|
return {
|
|
1069
1070
|
target: entitySummary(targetEntity),
|
|
1070
1071
|
mutation: args.mutation,
|
|
1071
|
-
|
|
1072
|
-
_summary: `Simulating adding [${addedTags.join(', ')}] to ${entityName(targetEntity)}. This structurally
|
|
1072
|
+
affectedSymbolsCount: fallout.length,
|
|
1073
|
+
_summary: `Simulating adding [${addedTags.join(', ')}] to ${entityName(targetEntity)}. This structurally impacts ${fallout.length} upstream symbols.`,
|
|
1073
1074
|
fallout,
|
|
1074
1075
|
};
|
|
1075
1076
|
}
|
|
1076
|
-
// ─── Tool: query_data_targets (
|
|
1077
|
+
// ─── Tool: query_data_targets (Data Flow Analysis) ──────────────
|
|
1077
1078
|
export function query_data_targets(args) {
|
|
1078
1079
|
const projErr = validateProject(args.project);
|
|
1079
1080
|
if (projErr)
|
|
@@ -1122,12 +1123,12 @@ export function query_data_targets(args) {
|
|
|
1122
1123
|
totalInteractions: readers.length + writers.length,
|
|
1123
1124
|
writersCount: writers.length,
|
|
1124
1125
|
readersCount: readers.length,
|
|
1125
|
-
_summary: `Found ${writers.length}
|
|
1126
|
+
_summary: `Found ${writers.length} symbols mutating and ${readers.length} symbols reading data target '${args.target_name}'`,
|
|
1126
1127
|
writers: writers.slice(0, 50),
|
|
1127
1128
|
readers: readers.slice(0, 50),
|
|
1128
1129
|
};
|
|
1129
1130
|
}
|
|
1130
|
-
// ─── Tool: find_exposure_leaks (
|
|
1131
|
+
// ─── Tool: find_exposure_leaks (Architectural Visibility) ───────
|
|
1131
1132
|
export function find_exposure_leaks(args) {
|
|
1132
1133
|
const projErr = validateProject(args?.project);
|
|
1133
1134
|
if (projErr)
|
|
@@ -1150,18 +1151,18 @@ export function find_exposure_leaks(args) {
|
|
|
1150
1151
|
leaks.push({
|
|
1151
1152
|
publicCaller: entitySummary(callerEntity),
|
|
1152
1153
|
privateCallee: entitySummary(calleeEntity),
|
|
1153
|
-
issue: `Exposure Leak: Public/API
|
|
1154
|
+
issue: `Exposure Leak: Public/API symbol directly calls deeply private internal implementation.`,
|
|
1154
1155
|
});
|
|
1155
1156
|
}
|
|
1156
1157
|
}
|
|
1157
1158
|
}
|
|
1158
1159
|
return {
|
|
1159
1160
|
totalLeaks: leaks.length,
|
|
1160
|
-
_summary: `Found ${leaks.length} architectural visibility leaks where public
|
|
1161
|
+
_summary: `Found ${leaks.length} architectural visibility leaks where public paths bypass internal boundaries`,
|
|
1161
1162
|
leaks: leaks.slice(0, 100),
|
|
1162
1163
|
};
|
|
1163
1164
|
}
|
|
1164
|
-
// ─── Tool: find_semantic_clones (
|
|
1165
|
+
// ─── Tool: find_semantic_clones (Logic Analysis) ────────────────
|
|
1165
1166
|
import { createHash } from 'crypto';
|
|
1166
1167
|
export function find_semantic_clones(args) {
|
|
1167
1168
|
const projErr = validateProject(args?.project);
|
|
@@ -1205,8 +1206,44 @@ export function find_semantic_clones(args) {
|
|
|
1205
1206
|
totalDuplicatedEntities += c.count;
|
|
1206
1207
|
return {
|
|
1207
1208
|
cloneGroupsFound: clones.length,
|
|
1208
|
-
totalDuplicatedEntities,
|
|
1209
|
-
_summary: `Found ${clones.length}
|
|
1209
|
+
totalDuplicatedSymbols: totalDuplicatedEntities,
|
|
1210
|
+
_summary: `Found ${clones.length} logic clone groups involving ${totalDuplicatedEntities} symbols`,
|
|
1210
1211
|
cloneGroups: clones.slice(0, 50),
|
|
1211
1212
|
};
|
|
1212
1213
|
}
|
|
1214
|
+
// ─── Tool: create_symbol (Virtual Entry) ─────────────────────────
|
|
1215
|
+
export function create_symbol(args) {
|
|
1216
|
+
const projErr = validateProject(args.project);
|
|
1217
|
+
if (projErr)
|
|
1218
|
+
return { error: projErr };
|
|
1219
|
+
const loader = getLoader();
|
|
1220
|
+
// Create a skeleton JSTF-T entity
|
|
1221
|
+
const virtualEntity = {
|
|
1222
|
+
id: args.id,
|
|
1223
|
+
_sourceFile: args.source_file,
|
|
1224
|
+
sourceFile: args.source_file,
|
|
1225
|
+
context: {
|
|
1226
|
+
layer: args.layer || 'other',
|
|
1227
|
+
visibility: 'public',
|
|
1228
|
+
module: path.basename(args.source_file, path.extname(args.source_file)),
|
|
1229
|
+
},
|
|
1230
|
+
struct: {
|
|
1231
|
+
name: args.id,
|
|
1232
|
+
type: 'pending_creation'
|
|
1233
|
+
},
|
|
1234
|
+
constraints: {},
|
|
1235
|
+
traits: { self: {} },
|
|
1236
|
+
runtime: { framework: 'agnostic' },
|
|
1237
|
+
edges: { calls: [], imports: [] },
|
|
1238
|
+
semantics: [],
|
|
1239
|
+
_virtual: true,
|
|
1240
|
+
_description: args.description
|
|
1241
|
+
};
|
|
1242
|
+
loader.registerVirtualEntity(virtualEntity, args.project);
|
|
1243
|
+
return {
|
|
1244
|
+
success: true,
|
|
1245
|
+
id: args.id,
|
|
1246
|
+
sourceFile: args.source_file,
|
|
1247
|
+
_summary: `Registered virtual symbol '${args.id}' in ${args.source_file}. Other tools can now reason about this symbol prior to its physical creation.`,
|
|
1248
|
+
};
|
|
1249
|
+
}
|
package/package.json
CHANGED