@cocaxcode/ai-context-inspector 0.1.0 → 0.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.
|
@@ -229,8 +229,8 @@ async function introspectServers(servers, timeout) {
|
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
// src/scanner/index.ts
|
|
232
|
-
import { readFile as
|
|
233
|
-
import { join as
|
|
232
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
233
|
+
import { join as join6, basename as basename2 } from "path";
|
|
234
234
|
import { resolve } from "path";
|
|
235
235
|
|
|
236
236
|
// src/scanner/context-files.ts
|
|
@@ -381,6 +381,50 @@ var AI_FILE_CATALOG = [
|
|
|
381
381
|
scope: "project",
|
|
382
382
|
description: "Directorio de configuraci\xF3n de Codex"
|
|
383
383
|
},
|
|
384
|
+
// ── OpenCode ──
|
|
385
|
+
{
|
|
386
|
+
path: "OPENCODE.md",
|
|
387
|
+
tool: "opencode",
|
|
388
|
+
type: "file",
|
|
389
|
+
scope: "project",
|
|
390
|
+
description: "Instrucciones de proyecto para OpenCode CLI"
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
path: ".opencode",
|
|
394
|
+
tool: "opencode",
|
|
395
|
+
type: "directory",
|
|
396
|
+
scope: "project",
|
|
397
|
+
description: "Directorio de configuraci\xF3n de OpenCode"
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
path: "opencode.json",
|
|
401
|
+
tool: "opencode",
|
|
402
|
+
type: "file",
|
|
403
|
+
scope: "project",
|
|
404
|
+
description: "Configuraci\xF3n de OpenCode"
|
|
405
|
+
},
|
|
406
|
+
// ── Roo Code (ex-Cline fork) ──
|
|
407
|
+
{
|
|
408
|
+
path: ".roo/rules",
|
|
409
|
+
tool: "roo",
|
|
410
|
+
type: "directory",
|
|
411
|
+
scope: "project",
|
|
412
|
+
description: "Directorio de reglas de Roo Code"
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
path: ".roorules",
|
|
416
|
+
tool: "roo",
|
|
417
|
+
type: "file",
|
|
418
|
+
scope: "project",
|
|
419
|
+
description: "Reglas de Roo Code (archivo \xFAnico)"
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
path: ".rooignore",
|
|
423
|
+
tool: "roo",
|
|
424
|
+
type: "file",
|
|
425
|
+
scope: "project",
|
|
426
|
+
description: "Archivos excluidos de Roo Code"
|
|
427
|
+
},
|
|
384
428
|
// ── Aider ──
|
|
385
429
|
{
|
|
386
430
|
path: ".aider.conf.yml",
|
|
@@ -487,6 +531,37 @@ var AI_FILE_CATALOG = [
|
|
|
487
531
|
scope: "project",
|
|
488
532
|
description: "Convenciones de c\xF3digo (multi-tool)"
|
|
489
533
|
},
|
|
534
|
+
// ── Tabnine ──
|
|
535
|
+
{
|
|
536
|
+
path: ".tabnine.yaml",
|
|
537
|
+
tool: "tabnine",
|
|
538
|
+
type: "file",
|
|
539
|
+
scope: "project",
|
|
540
|
+
description: "Configuraci\xF3n de Tabnine AI"
|
|
541
|
+
},
|
|
542
|
+
// ── Sourcegraph / Cody ──
|
|
543
|
+
{
|
|
544
|
+
path: ".sourcegraph",
|
|
545
|
+
tool: "sourcegraph",
|
|
546
|
+
type: "directory",
|
|
547
|
+
scope: "project",
|
|
548
|
+
description: "Directorio de Sourcegraph Cody"
|
|
549
|
+
},
|
|
550
|
+
// ── Continue.dev (project level) ──
|
|
551
|
+
{
|
|
552
|
+
path: ".continuerules",
|
|
553
|
+
tool: "continue",
|
|
554
|
+
type: "file",
|
|
555
|
+
scope: "project",
|
|
556
|
+
description: "Reglas de Continue.dev"
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
path: ".continue/config.yaml",
|
|
560
|
+
tool: "continue",
|
|
561
|
+
type: "file",
|
|
562
|
+
scope: "project",
|
|
563
|
+
description: "Configuraci\xF3n de Continue.dev"
|
|
564
|
+
},
|
|
490
565
|
// ── User-level entries ──
|
|
491
566
|
{
|
|
492
567
|
path: "~/.claude",
|
|
@@ -564,11 +639,25 @@ var AI_FILE_CATALOG = [
|
|
|
564
639
|
type: "directory",
|
|
565
640
|
scope: "user",
|
|
566
641
|
description: "Configuraci\xF3n global de Codeium/Windsurf"
|
|
642
|
+
},
|
|
643
|
+
{
|
|
644
|
+
path: "~/.opencode",
|
|
645
|
+
tool: "opencode",
|
|
646
|
+
type: "directory",
|
|
647
|
+
scope: "user",
|
|
648
|
+
description: "Configuraci\xF3n global de OpenCode CLI"
|
|
649
|
+
},
|
|
650
|
+
{
|
|
651
|
+
path: "~/.tabnine",
|
|
652
|
+
tool: "tabnine",
|
|
653
|
+
type: "directory",
|
|
654
|
+
scope: "user",
|
|
655
|
+
description: "Configuraci\xF3n global de Tabnine"
|
|
567
656
|
}
|
|
568
657
|
];
|
|
569
658
|
|
|
570
659
|
// src/scanner/context-files.ts
|
|
571
|
-
var PREVIEW_MAX_CHARS =
|
|
660
|
+
var PREVIEW_MAX_CHARS = 2e3;
|
|
572
661
|
function resolveHome(p) {
|
|
573
662
|
return p.startsWith("~/") ? join2(homedir2(), p.slice(2)) : p;
|
|
574
663
|
}
|
|
@@ -674,28 +763,67 @@ async function scanContextFiles(config) {
|
|
|
674
763
|
}
|
|
675
764
|
|
|
676
765
|
// src/scanner/skills.ts
|
|
677
|
-
import { readFile as readFile3, readdir as readdir2, stat as stat2 } from "fs/promises";
|
|
766
|
+
import { readFile as readFile3, readdir as readdir2, stat as stat2, lstat } from "fs/promises";
|
|
678
767
|
import { join as join3 } from "path";
|
|
679
768
|
import { homedir as homedir3 } from "os";
|
|
680
769
|
var SKILL_DIRS_PROJECT = [".claude/skills"];
|
|
681
770
|
var SKILL_DIRS_USER = ["~/.claude/skills"];
|
|
771
|
+
function parseFrontmatter(content) {
|
|
772
|
+
const match = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
773
|
+
if (!match) return null;
|
|
774
|
+
const result = {};
|
|
775
|
+
let currentKey = "";
|
|
776
|
+
let currentValue = "";
|
|
777
|
+
for (const line of match[1].split("\n")) {
|
|
778
|
+
const kvMatch = line.match(/^(\w[\w-]*):\s*(.*)$/);
|
|
779
|
+
if (kvMatch) {
|
|
780
|
+
if (currentKey) result[currentKey] = currentValue.trim();
|
|
781
|
+
currentKey = kvMatch[1];
|
|
782
|
+
currentValue = kvMatch[2].replace(/^["'>-]\s*/, "").replace(/"$/, "");
|
|
783
|
+
} else if (currentKey && line.match(/^\s+/)) {
|
|
784
|
+
currentValue += " " + line.trim();
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
if (currentKey) result[currentKey] = currentValue.trim();
|
|
788
|
+
return result;
|
|
789
|
+
}
|
|
682
790
|
async function scanSkillDir(dirPath, scope) {
|
|
683
791
|
const skills = [];
|
|
684
792
|
try {
|
|
685
793
|
const entries = await readdir2(dirPath, { withFileTypes: true });
|
|
686
794
|
for (const entry of entries) {
|
|
687
|
-
if (
|
|
688
|
-
|
|
689
|
-
|
|
795
|
+
if (entry.name.startsWith("_")) continue;
|
|
796
|
+
const entryPath = join3(dirPath, entry.name);
|
|
797
|
+
let isDir = entry.isDirectory();
|
|
798
|
+
if (entry.isSymbolicLink()) {
|
|
799
|
+
try {
|
|
800
|
+
const targetStat = await stat2(entryPath);
|
|
801
|
+
isDir = targetStat.isDirectory();
|
|
802
|
+
} catch {
|
|
803
|
+
continue;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
if (!isDir) continue;
|
|
807
|
+
const skillMdPath = join3(entryPath, "SKILL.md");
|
|
690
808
|
try {
|
|
691
809
|
await stat2(skillMdPath);
|
|
692
810
|
const content = await readFile3(skillMdPath, "utf-8");
|
|
693
811
|
let description;
|
|
694
|
-
|
|
695
|
-
if (purposeMatch) {
|
|
696
|
-
description = purposeMatch[1].trim().slice(0, 200);
|
|
697
|
-
}
|
|
812
|
+
let name = entry.name;
|
|
698
813
|
const triggers = [];
|
|
814
|
+
const frontmatter = parseFrontmatter(content);
|
|
815
|
+
if (frontmatter) {
|
|
816
|
+
if (frontmatter.name) name = frontmatter.name;
|
|
817
|
+
if (frontmatter.description) {
|
|
818
|
+
description = frontmatter.description.slice(0, 200);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
if (!description) {
|
|
822
|
+
const purposeMatch = content.match(/##\s*Purpose\s*\n+(.+)/i);
|
|
823
|
+
if (purposeMatch) {
|
|
824
|
+
description = purposeMatch[1].trim().slice(0, 200);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
699
827
|
const triggerMatch = content.match(
|
|
700
828
|
/(?:trigger|triggers?):\s*(.+)/gi
|
|
701
829
|
);
|
|
@@ -705,12 +833,19 @@ async function scanSkillDir(dirPath, scope) {
|
|
|
705
833
|
if (value) triggers.push(value);
|
|
706
834
|
}
|
|
707
835
|
}
|
|
836
|
+
let isSymlink = false;
|
|
837
|
+
try {
|
|
838
|
+
const lstats = await lstat(entryPath);
|
|
839
|
+
isSymlink = lstats.isSymbolicLink();
|
|
840
|
+
} catch {
|
|
841
|
+
}
|
|
708
842
|
skills.push({
|
|
709
|
-
name
|
|
843
|
+
name,
|
|
710
844
|
path: skillMdPath,
|
|
711
845
|
scope,
|
|
712
846
|
description,
|
|
713
|
-
triggers: triggers.length > 0 ? triggers : void 0
|
|
847
|
+
triggers: triggers.length > 0 ? triggers : void 0,
|
|
848
|
+
isSymlink
|
|
714
849
|
});
|
|
715
850
|
} catch {
|
|
716
851
|
}
|
|
@@ -776,25 +911,119 @@ async function scanSkills(config) {
|
|
|
776
911
|
return { skills: allSkills, warnings };
|
|
777
912
|
}
|
|
778
913
|
|
|
779
|
-
// src/scanner/
|
|
780
|
-
import { readdir as readdir3, stat as stat3 } from "fs/promises";
|
|
914
|
+
// src/scanner/agents.ts
|
|
915
|
+
import { readFile as readFile4, readdir as readdir3, stat as stat3 } from "fs/promises";
|
|
781
916
|
import { join as join4 } from "path";
|
|
917
|
+
import { homedir as homedir4 } from "os";
|
|
918
|
+
var AGENT_DIRS_PROJECT = [".claude/agents"];
|
|
919
|
+
var AGENT_DIRS_USER = ["~/.claude/agents"];
|
|
920
|
+
var AGENT_MEMORY_DIRS_USER = ["~/.claude/agent-memory"];
|
|
921
|
+
function parseFrontmatter2(content) {
|
|
922
|
+
const match = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
923
|
+
if (!match) return null;
|
|
924
|
+
const result = {};
|
|
925
|
+
let currentKey = "";
|
|
926
|
+
let currentValue = "";
|
|
927
|
+
for (const line of match[1].split("\n")) {
|
|
928
|
+
const kvMatch = line.match(/^(\w[\w-]*):\s*(.*)$/);
|
|
929
|
+
if (kvMatch) {
|
|
930
|
+
if (currentKey) result[currentKey] = currentValue.trim();
|
|
931
|
+
currentKey = kvMatch[1];
|
|
932
|
+
currentValue = kvMatch[2].replace(/^["'>-]\s*/, "").replace(/"$/, "");
|
|
933
|
+
} else if (currentKey && line.match(/^\s+/)) {
|
|
934
|
+
currentValue += " " + line.trim();
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
if (currentKey) result[currentKey] = currentValue.trim();
|
|
938
|
+
return result;
|
|
939
|
+
}
|
|
940
|
+
async function scanAgentDir(dirPath, memoryDirPath, scope) {
|
|
941
|
+
const agents = [];
|
|
942
|
+
try {
|
|
943
|
+
const entries = await readdir3(dirPath);
|
|
944
|
+
for (const entry of entries) {
|
|
945
|
+
if (!entry.endsWith(".md")) continue;
|
|
946
|
+
const filePath = join4(dirPath, entry);
|
|
947
|
+
try {
|
|
948
|
+
const s = await stat3(filePath);
|
|
949
|
+
if (!s.isFile()) continue;
|
|
950
|
+
const content = await readFile4(filePath, "utf-8");
|
|
951
|
+
const agentName = entry.replace(/\.md$/, "");
|
|
952
|
+
let name = agentName;
|
|
953
|
+
let description;
|
|
954
|
+
let model;
|
|
955
|
+
const frontmatter = parseFrontmatter2(content);
|
|
956
|
+
if (frontmatter) {
|
|
957
|
+
if (frontmatter.name) name = frontmatter.name;
|
|
958
|
+
if (frontmatter.description) {
|
|
959
|
+
description = frontmatter.description.slice(0, 300);
|
|
960
|
+
}
|
|
961
|
+
if (frontmatter.model) model = frontmatter.model;
|
|
962
|
+
}
|
|
963
|
+
let hasMemory = false;
|
|
964
|
+
if (memoryDirPath) {
|
|
965
|
+
try {
|
|
966
|
+
await stat3(join4(memoryDirPath, agentName));
|
|
967
|
+
hasMemory = true;
|
|
968
|
+
} catch {
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
agents.push({
|
|
972
|
+
name,
|
|
973
|
+
path: filePath,
|
|
974
|
+
scope,
|
|
975
|
+
description,
|
|
976
|
+
model,
|
|
977
|
+
hasMemory
|
|
978
|
+
});
|
|
979
|
+
} catch {
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
} catch {
|
|
983
|
+
}
|
|
984
|
+
return agents;
|
|
985
|
+
}
|
|
986
|
+
async function scanAgents(config) {
|
|
987
|
+
const allAgents = [];
|
|
988
|
+
const warnings = [];
|
|
989
|
+
for (const dir of AGENT_DIRS_PROJECT) {
|
|
990
|
+
const fullPath = join4(config.dir, dir);
|
|
991
|
+
const found = await scanAgentDir(fullPath, null, "project");
|
|
992
|
+
allAgents.push(...found);
|
|
993
|
+
}
|
|
994
|
+
if (config.includeUser) {
|
|
995
|
+
for (let i = 0; i < AGENT_DIRS_USER.length; i++) {
|
|
996
|
+
const dir = AGENT_DIRS_USER[i];
|
|
997
|
+
const memDir = AGENT_MEMORY_DIRS_USER[i];
|
|
998
|
+
const fullPath = dir.startsWith("~/") ? join4(homedir4(), dir.slice(2)) : dir;
|
|
999
|
+
const memPath = memDir?.startsWith("~/") ? join4(homedir4(), memDir.slice(2)) : memDir ?? null;
|
|
1000
|
+
const found = await scanAgentDir(fullPath, memPath, "user");
|
|
1001
|
+
allAgents.push(...found);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
return { agents: allAgents, warnings };
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
// src/scanner/memories.ts
|
|
1008
|
+
import { readFile as readFile5, readdir as readdir4, stat as stat4 } from "fs/promises";
|
|
1009
|
+
import { join as join5, basename } from "path";
|
|
1010
|
+
import { homedir as homedir5 } from "os";
|
|
782
1011
|
async function detectOpenspec(dir) {
|
|
783
|
-
const openspecDir =
|
|
1012
|
+
const openspecDir = join5(dir, "openspec");
|
|
784
1013
|
try {
|
|
785
|
-
const s = await
|
|
1014
|
+
const s = await stat4(openspecDir);
|
|
786
1015
|
if (!s.isDirectory()) return null;
|
|
787
1016
|
let specsCount = 0;
|
|
788
1017
|
let changesCount = 0;
|
|
789
1018
|
try {
|
|
790
|
-
const specsDir =
|
|
791
|
-
const specs = await
|
|
1019
|
+
const specsDir = join5(openspecDir, "specs");
|
|
1020
|
+
const specs = await readdir4(specsDir);
|
|
792
1021
|
specsCount = specs.filter((f) => !f.startsWith(".")).length;
|
|
793
1022
|
} catch {
|
|
794
1023
|
}
|
|
795
1024
|
try {
|
|
796
|
-
const changesDir =
|
|
797
|
-
const changes = await
|
|
1025
|
+
const changesDir = join5(openspecDir, "changes");
|
|
1026
|
+
const changes = await readdir4(changesDir);
|
|
798
1027
|
changesCount = changes.filter(
|
|
799
1028
|
(f) => !f.startsWith(".") && f !== "archive"
|
|
800
1029
|
).length;
|
|
@@ -812,13 +1041,13 @@ async function detectOpenspec(dir) {
|
|
|
812
1041
|
}
|
|
813
1042
|
}
|
|
814
1043
|
async function detectAtl(dir) {
|
|
815
|
-
const atlDir =
|
|
1044
|
+
const atlDir = join5(dir, ".atl");
|
|
816
1045
|
try {
|
|
817
|
-
const s = await
|
|
1046
|
+
const s = await stat4(atlDir);
|
|
818
1047
|
if (!s.isDirectory()) return null;
|
|
819
1048
|
let files = [];
|
|
820
1049
|
try {
|
|
821
|
-
files = await
|
|
1050
|
+
files = await readdir4(atlDir);
|
|
822
1051
|
} catch {
|
|
823
1052
|
}
|
|
824
1053
|
return {
|
|
@@ -832,7 +1061,7 @@ async function detectAtl(dir) {
|
|
|
832
1061
|
return null;
|
|
833
1062
|
}
|
|
834
1063
|
}
|
|
835
|
-
function
|
|
1064
|
+
function detectEngramMcp(mcpServers) {
|
|
836
1065
|
const engram = mcpServers.find(
|
|
837
1066
|
(s) => s.name.toLowerCase().includes("engram") || s.config.command && s.config.command.includes("engram") || s.config.args && s.config.args.some((a) => a.toLowerCase().includes("engram"))
|
|
838
1067
|
);
|
|
@@ -844,6 +1073,99 @@ function detectEngram(mcpServers) {
|
|
|
844
1073
|
details: { server: engram.name }
|
|
845
1074
|
};
|
|
846
1075
|
}
|
|
1076
|
+
async function detectEngramPlugin() {
|
|
1077
|
+
try {
|
|
1078
|
+
const settingsPath = join5(homedir5(), ".claude", "settings.json");
|
|
1079
|
+
const content = await readFile5(settingsPath, "utf-8");
|
|
1080
|
+
const settings = JSON.parse(content);
|
|
1081
|
+
if (!settings.enabledPlugins) return null;
|
|
1082
|
+
const engramKey = Object.keys(settings.enabledPlugins).find(
|
|
1083
|
+
(k) => k.toLowerCase().includes("engram") && settings.enabledPlugins[k]
|
|
1084
|
+
);
|
|
1085
|
+
if (!engramKey) return null;
|
|
1086
|
+
return {
|
|
1087
|
+
type: "engram",
|
|
1088
|
+
source: "plugin",
|
|
1089
|
+
status: "active",
|
|
1090
|
+
details: { plugin: engramKey }
|
|
1091
|
+
};
|
|
1092
|
+
} catch {
|
|
1093
|
+
return null;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
async function detectClaudeMemories(_dir) {
|
|
1097
|
+
const memories = [];
|
|
1098
|
+
const projectsDir = join5(homedir5(), ".claude", "projects");
|
|
1099
|
+
try {
|
|
1100
|
+
const entries = await readdir4(projectsDir);
|
|
1101
|
+
for (const entry of entries) {
|
|
1102
|
+
const memoryPath = join5(projectsDir, entry, "memory", "MEMORY.md");
|
|
1103
|
+
try {
|
|
1104
|
+
const s = await stat4(memoryPath);
|
|
1105
|
+
if (!s.isFile()) continue;
|
|
1106
|
+
const content = await readFile5(memoryPath, "utf-8");
|
|
1107
|
+
const previewLength = Math.min(content.length, 300);
|
|
1108
|
+
const preview = content.slice(0, previewLength);
|
|
1109
|
+
const projectName = entry.replace(/--/g, "/").replace(/-/g, "/");
|
|
1110
|
+
memories.push({
|
|
1111
|
+
type: "claude-memory",
|
|
1112
|
+
path: memoryPath,
|
|
1113
|
+
source: "filesystem",
|
|
1114
|
+
status: "active",
|
|
1115
|
+
details: {
|
|
1116
|
+
project: projectName,
|
|
1117
|
+
size: s.size,
|
|
1118
|
+
preview
|
|
1119
|
+
}
|
|
1120
|
+
});
|
|
1121
|
+
} catch {
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
} catch {
|
|
1125
|
+
}
|
|
1126
|
+
return memories;
|
|
1127
|
+
}
|
|
1128
|
+
async function detectAgentMemories() {
|
|
1129
|
+
const memories = [];
|
|
1130
|
+
const agentMemDir = join5(homedir5(), ".claude", "agent-memory");
|
|
1131
|
+
try {
|
|
1132
|
+
const entries = await readdir4(agentMemDir, { withFileTypes: true });
|
|
1133
|
+
for (const entry of entries) {
|
|
1134
|
+
if (entry.isDirectory()) {
|
|
1135
|
+
const memDir = join5(agentMemDir, entry.name);
|
|
1136
|
+
try {
|
|
1137
|
+
const files = await readdir4(memDir);
|
|
1138
|
+
const mdFiles = files.filter((f) => f.endsWith(".md"));
|
|
1139
|
+
if (mdFiles.length > 0) {
|
|
1140
|
+
memories.push({
|
|
1141
|
+
type: "agent-memory",
|
|
1142
|
+
path: memDir,
|
|
1143
|
+
source: "filesystem",
|
|
1144
|
+
status: "active",
|
|
1145
|
+
details: {
|
|
1146
|
+
agent: entry.name,
|
|
1147
|
+
files: mdFiles.length
|
|
1148
|
+
}
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
} catch {
|
|
1152
|
+
}
|
|
1153
|
+
} else if (entry.name.endsWith(".md")) {
|
|
1154
|
+
memories.push({
|
|
1155
|
+
type: "agent-memory",
|
|
1156
|
+
path: join5(agentMemDir, entry.name),
|
|
1157
|
+
source: "filesystem",
|
|
1158
|
+
status: "active",
|
|
1159
|
+
details: {
|
|
1160
|
+
agent: basename(entry.name, ".md")
|
|
1161
|
+
}
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
} catch {
|
|
1166
|
+
}
|
|
1167
|
+
return memories;
|
|
1168
|
+
}
|
|
847
1169
|
async function scanMemories(config, mcpServers) {
|
|
848
1170
|
const memories = [];
|
|
849
1171
|
const warnings = [];
|
|
@@ -851,20 +1173,31 @@ async function scanMemories(config, mcpServers) {
|
|
|
851
1173
|
if (openspec) memories.push(openspec);
|
|
852
1174
|
const atl = await detectAtl(config.dir);
|
|
853
1175
|
if (atl) memories.push(atl);
|
|
854
|
-
const
|
|
855
|
-
if (
|
|
1176
|
+
const engramMcp = detectEngramMcp(mcpServers);
|
|
1177
|
+
if (engramMcp) {
|
|
1178
|
+
memories.push(engramMcp);
|
|
1179
|
+
} else if (config.includeUser) {
|
|
1180
|
+
const engramPlugin = await detectEngramPlugin();
|
|
1181
|
+
if (engramPlugin) memories.push(engramPlugin);
|
|
1182
|
+
}
|
|
1183
|
+
if (config.includeUser) {
|
|
1184
|
+
const claudeMemories = await detectClaudeMemories(config.dir);
|
|
1185
|
+
memories.push(...claudeMemories);
|
|
1186
|
+
const agentMemories = await detectAgentMemories();
|
|
1187
|
+
memories.push(...agentMemories);
|
|
1188
|
+
}
|
|
856
1189
|
return { memories, warnings };
|
|
857
1190
|
}
|
|
858
1191
|
|
|
859
1192
|
// src/scanner/index.ts
|
|
860
1193
|
async function detectProjectName(dir) {
|
|
861
1194
|
try {
|
|
862
|
-
const pkg = await
|
|
1195
|
+
const pkg = await readFile6(join6(dir, "package.json"), "utf-8");
|
|
863
1196
|
const parsed = JSON.parse(pkg);
|
|
864
1197
|
if (parsed.name) return parsed.name;
|
|
865
1198
|
} catch {
|
|
866
1199
|
}
|
|
867
|
-
return
|
|
1200
|
+
return basename2(dir);
|
|
868
1201
|
}
|
|
869
1202
|
async function runAllScanners(config) {
|
|
870
1203
|
const start = performance.now();
|
|
@@ -876,14 +1209,16 @@ async function runAllScanners(config) {
|
|
|
876
1209
|
path: absDir,
|
|
877
1210
|
scannedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
878
1211
|
};
|
|
879
|
-
const [contextResult, mcpResult, skillsResult] = await Promise.all([
|
|
1212
|
+
const [contextResult, mcpResult, skillsResult, agentsResult] = await Promise.all([
|
|
880
1213
|
scanContextFiles({ ...config, dir: absDir }),
|
|
881
1214
|
scanMcpConfigs({ dir: absDir, includeUser: config.includeUser }),
|
|
882
|
-
scanSkills({ ...config, dir: absDir })
|
|
1215
|
+
scanSkills({ ...config, dir: absDir }),
|
|
1216
|
+
scanAgents({ ...config, dir: absDir })
|
|
883
1217
|
]);
|
|
884
1218
|
warnings.push(...contextResult.warnings);
|
|
885
1219
|
warnings.push(...mcpResult.warnings);
|
|
886
1220
|
warnings.push(...skillsResult.warnings);
|
|
1221
|
+
warnings.push(...agentsResult.warnings);
|
|
887
1222
|
if (config.introspect && mcpResult.servers.length > 0) {
|
|
888
1223
|
await introspectServers(mcpResult.servers, config.timeout);
|
|
889
1224
|
}
|
|
@@ -898,6 +1233,7 @@ async function runAllScanners(config) {
|
|
|
898
1233
|
contextFiles: contextResult.files,
|
|
899
1234
|
mcpServers: mcpResult.servers,
|
|
900
1235
|
skills: skillsResult.skills,
|
|
1236
|
+
agents: agentsResult.agents,
|
|
901
1237
|
memories: memoriesResult.memories,
|
|
902
1238
|
warnings,
|
|
903
1239
|
scanDuration
|
|
@@ -1000,6 +1336,7 @@ body {
|
|
|
1000
1336
|
.badge--green { border-color: var(--green); color: var(--green); }
|
|
1001
1337
|
.badge--purple { border-color: var(--purple); color: var(--purple); }
|
|
1002
1338
|
.badge--orange { border-color: var(--orange); color: var(--orange); }
|
|
1339
|
+
.badge--blue { border-color: #4285f4; color: #4285f4; }
|
|
1003
1340
|
|
|
1004
1341
|
/* \u2500\u2500 Search \u2500\u2500 */
|
|
1005
1342
|
.search-bar {
|
|
@@ -1139,6 +1476,10 @@ body {
|
|
|
1139
1476
|
.tool-badge--augment { background: #9c27b020; color: #9c27b0; border: 1px solid #9c27b040; }
|
|
1140
1477
|
.tool-badge--replit { background: #f2620020; color: #f26200; border: 1px solid #f2620040; }
|
|
1141
1478
|
.tool-badge--firebase { background: #ffca2820; color: #ffca28; border: 1px solid #ffca2840; }
|
|
1479
|
+
.tool-badge--opencode { background: #22c55e20; color: #22c55e; border: 1px solid #22c55e40; }
|
|
1480
|
+
.tool-badge--roo { background: #06b6d420; color: #06b6d4; border: 1px solid #06b6d440; }
|
|
1481
|
+
.tool-badge--tabnine { background: #e8596820; color: #e85968; border: 1px solid #e8596840; }
|
|
1482
|
+
.tool-badge--sourcegraph { background: #a112ff20; color: #a112ff; border: 1px solid #a112ff40; }
|
|
1142
1483
|
.tool-badge--vscode { background: #007acc20; color: #007acc; border: 1px solid #007acc40; }
|
|
1143
1484
|
.tool-badge--universal { background: #78909c20; color: #78909c; border: 1px solid #78909c40; }
|
|
1144
1485
|
|
|
@@ -1340,6 +1681,7 @@ function renderHeader(project, summary, scanDuration) {
|
|
|
1340
1681
|
<span class="badge badge--green">${summary.totalTools} tools</span>
|
|
1341
1682
|
<span class="badge badge--purple">${summary.totalFiles} archivos</span>
|
|
1342
1683
|
<span class="badge badge--orange">${summary.totalSkills} skills</span>
|
|
1684
|
+
<span class="badge badge--blue">${summary.totalAgents} agents</span>
|
|
1343
1685
|
<span class="badge badge--accent">${summary.totalMemories} memorias</span>
|
|
1344
1686
|
</div>
|
|
1345
1687
|
</header>`;
|
|
@@ -1469,6 +1811,34 @@ function renderSkills(skills) {
|
|
|
1469
1811
|
<div class="section-content">${items}</div>
|
|
1470
1812
|
</div>`;
|
|
1471
1813
|
}
|
|
1814
|
+
function renderAgents(agents) {
|
|
1815
|
+
if (agents.length === 0) return "";
|
|
1816
|
+
const items = agents.map((a) => {
|
|
1817
|
+
const modelHtml = a.model ? `<span class="badge badge--green">${esc(a.model)}</span>` : "";
|
|
1818
|
+
const memoryHtml = a.hasMemory ? '<span class="badge badge--accent">memoria</span>' : "";
|
|
1819
|
+
return `
|
|
1820
|
+
<div class="card" data-searchable="${esc(a.name + " " + (a.description ?? ""))}">
|
|
1821
|
+
<div class="card-title">
|
|
1822
|
+
${esc(a.name)}
|
|
1823
|
+
${modelHtml}
|
|
1824
|
+
${memoryHtml}
|
|
1825
|
+
<span class="scope-badge scope-badge--${a.scope}">${a.scope}</span>
|
|
1826
|
+
</div>
|
|
1827
|
+
${a.description ? `<div class="card-meta">${esc(a.description)}</div>` : ""}
|
|
1828
|
+
</div>`;
|
|
1829
|
+
}).join("");
|
|
1830
|
+
return `
|
|
1831
|
+
<div class="section">
|
|
1832
|
+
<div class="section-header">
|
|
1833
|
+
<h2>Agents</h2>
|
|
1834
|
+
<div>
|
|
1835
|
+
<span class="count">${agents.length}</span>
|
|
1836
|
+
<span class="arrow">▼</span>
|
|
1837
|
+
</div>
|
|
1838
|
+
</div>
|
|
1839
|
+
<div class="section-content">${items}</div>
|
|
1840
|
+
</div>`;
|
|
1841
|
+
}
|
|
1472
1842
|
function renderMemories(memories) {
|
|
1473
1843
|
if (memories.length === 0) return "";
|
|
1474
1844
|
const items = memories.map((m) => {
|
|
@@ -1517,17 +1887,19 @@ function computeSummary(result) {
|
|
|
1517
1887
|
),
|
|
1518
1888
|
totalFiles: result.contextFiles.length,
|
|
1519
1889
|
totalSkills: result.skills.length,
|
|
1890
|
+
totalAgents: result.agents.length,
|
|
1520
1891
|
totalMemories: result.memories.length
|
|
1521
1892
|
};
|
|
1522
1893
|
}
|
|
1523
1894
|
function generateHtml(result) {
|
|
1524
1895
|
const summary = computeSummary(result);
|
|
1525
|
-
const isEmpty = summary.totalMcpServers === 0 && summary.totalFiles === 0 && summary.totalSkills === 0 && summary.totalMemories === 0;
|
|
1896
|
+
const isEmpty = summary.totalMcpServers === 0 && summary.totalFiles === 0 && summary.totalSkills === 0 && summary.totalAgents === 0 && summary.totalMemories === 0;
|
|
1526
1897
|
const header = renderHeader(result.project, summary, result.scanDuration);
|
|
1527
1898
|
const content = isEmpty ? renderEmptyState() : [
|
|
1528
1899
|
renderMcpServers(result.mcpServers),
|
|
1529
1900
|
renderContextFiles(result.contextFiles),
|
|
1530
1901
|
renderSkills(result.skills),
|
|
1902
|
+
renderAgents(result.agents),
|
|
1531
1903
|
renderMemories(result.memories)
|
|
1532
1904
|
].join("");
|
|
1533
1905
|
return `<!DOCTYPE html>
|
|
@@ -1542,7 +1914,7 @@ function generateHtml(result) {
|
|
|
1542
1914
|
<div class="container">
|
|
1543
1915
|
${header}
|
|
1544
1916
|
<div class="search-bar">
|
|
1545
|
-
<input type="text" id="search-input" placeholder="Buscar tools, archivos, skills..." />
|
|
1917
|
+
<input type="text" id="search-input" placeholder="Buscar tools, archivos, skills, agents..." />
|
|
1546
1918
|
</div>
|
|
1547
1919
|
${content}
|
|
1548
1920
|
<footer class="footer">
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
generateHtml,
|
|
4
4
|
runAllScanners
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-UFTX3Z5M.js";
|
|
6
6
|
|
|
7
7
|
// src/cli.ts
|
|
8
8
|
import { parseArgs } from "util";
|
|
@@ -97,7 +97,7 @@ async function main() {
|
|
|
97
97
|
const options = parseCliArgs(process.argv.slice(2));
|
|
98
98
|
if (options.mcp) {
|
|
99
99
|
const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
|
|
100
|
-
const { createServer } = await import("./server-
|
|
100
|
+
const { createServer } = await import("./server-AOHGLJDI.js");
|
|
101
101
|
const server = createServer();
|
|
102
102
|
const transport = new StdioServerTransport();
|
|
103
103
|
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cocaxcode/ai-context-inspector",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Scan any project to discover its complete AI ecosystem: MCP servers, tools, context files, skills, memories. Generates an interactive HTML dashboard. Works as CLI and MCP server.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|