@cubis/foundry 0.3.14 → 0.3.15
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/bin/cubis.js +396 -40
- package/package.json +2 -1
package/bin/cubis.js
CHANGED
|
@@ -172,6 +172,54 @@ const TECH_LANGUAGE_BY_EXTENSION = new Map([
|
|
|
172
172
|
[".sh", "Shell"],
|
|
173
173
|
[".ps1", "PowerShell"]
|
|
174
174
|
]);
|
|
175
|
+
const TECH_PACKAGE_PREVIEW_LIMIT = 40;
|
|
176
|
+
const TECH_JS_FRAMEWORK_SIGNALS = [
|
|
177
|
+
["next", "Next.js"],
|
|
178
|
+
["react", "React"],
|
|
179
|
+
["vue", "Vue"],
|
|
180
|
+
["nuxt", "Nuxt"],
|
|
181
|
+
["svelte", "Svelte"],
|
|
182
|
+
["@nestjs/core", "NestJS"],
|
|
183
|
+
["express", "Express"],
|
|
184
|
+
["fastify", "Fastify"],
|
|
185
|
+
["hono", "Hono"],
|
|
186
|
+
["tailwindcss", "Tailwind CSS"],
|
|
187
|
+
["prisma", "Prisma"],
|
|
188
|
+
["drizzle-orm", "Drizzle ORM"],
|
|
189
|
+
["mongoose", "Mongoose"],
|
|
190
|
+
["typeorm", "TypeORM"],
|
|
191
|
+
["@playwright/test", "Playwright"],
|
|
192
|
+
["vitest", "Vitest"],
|
|
193
|
+
["jest", "Jest"],
|
|
194
|
+
["cypress", "Cypress"]
|
|
195
|
+
];
|
|
196
|
+
const TECH_DART_FRAMEWORK_SIGNALS = [
|
|
197
|
+
["flutter_riverpod", "Riverpod"],
|
|
198
|
+
["riverpod", "Riverpod"],
|
|
199
|
+
["go_router", "go_router"],
|
|
200
|
+
["dio", "Dio"],
|
|
201
|
+
["freezed", "Freezed"],
|
|
202
|
+
["bloc", "BLoC"]
|
|
203
|
+
];
|
|
204
|
+
const TECH_GO_FRAMEWORK_SIGNALS = [
|
|
205
|
+
["github.com/gofiber/fiber/v2", "Go Fiber"],
|
|
206
|
+
["github.com/gin-gonic/gin", "Gin"],
|
|
207
|
+
["github.com/labstack/echo/v4", "Echo"],
|
|
208
|
+
["github.com/go-chi/chi/v5", "Chi"]
|
|
209
|
+
];
|
|
210
|
+
const TECH_PYTHON_FRAMEWORK_SIGNALS = [
|
|
211
|
+
["fastapi", "FastAPI"],
|
|
212
|
+
["django", "Django"],
|
|
213
|
+
["flask", "Flask"],
|
|
214
|
+
["pydantic", "Pydantic"],
|
|
215
|
+
["sqlalchemy", "SQLAlchemy"]
|
|
216
|
+
];
|
|
217
|
+
const TECH_RUST_FRAMEWORK_SIGNALS = [
|
|
218
|
+
["axum", "Axum"],
|
|
219
|
+
["actix-web", "Actix Web"],
|
|
220
|
+
["rocket", "Rocket"],
|
|
221
|
+
["tokio", "Tokio"]
|
|
222
|
+
];
|
|
175
223
|
|
|
176
224
|
function platformInstallsCustomAgents(platformId) {
|
|
177
225
|
const profile = WORKFLOW_PROFILES[platformId];
|
|
@@ -585,6 +633,198 @@ async function upsertEngineeringRulesBlock({
|
|
|
585
633
|
};
|
|
586
634
|
}
|
|
587
635
|
|
|
636
|
+
function normalizeTechPackageName(value) {
|
|
637
|
+
if (value === undefined || value === null) return null;
|
|
638
|
+
const normalized = String(value).trim().replace(/^['"]|['"]$/g, "").toLowerCase();
|
|
639
|
+
return normalized || null;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
function parseTomlSections(content) {
|
|
643
|
+
const sections = new Map();
|
|
644
|
+
let currentSection = "";
|
|
645
|
+
sections.set(currentSection, []);
|
|
646
|
+
|
|
647
|
+
for (const line of content.split(/\r?\n/)) {
|
|
648
|
+
const sectionMatch = line.match(/^\s*\[([^\]]+)\]\s*$/);
|
|
649
|
+
if (sectionMatch) {
|
|
650
|
+
currentSection = sectionMatch[1].trim();
|
|
651
|
+
if (!sections.has(currentSection)) sections.set(currentSection, []);
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
sections.get(currentSection).push(line);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
return sections;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
function parsePubspecDependencyNames(content) {
|
|
661
|
+
const packages = new Set();
|
|
662
|
+
let currentSection = null;
|
|
663
|
+
|
|
664
|
+
for (const rawLine of content.split(/\r?\n/)) {
|
|
665
|
+
const line = rawLine.replace(/\t/g, " ");
|
|
666
|
+
const trimmed = line.trim();
|
|
667
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
668
|
+
|
|
669
|
+
if (!line.startsWith(" ")) {
|
|
670
|
+
const sectionMatch = trimmed.match(/^([a-zA-Z0-9_]+):\s*$/);
|
|
671
|
+
if (!sectionMatch) {
|
|
672
|
+
currentSection = null;
|
|
673
|
+
continue;
|
|
674
|
+
}
|
|
675
|
+
currentSection = sectionMatch[1];
|
|
676
|
+
continue;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
if (currentSection !== "dependencies" && currentSection !== "dev_dependencies") continue;
|
|
680
|
+
const depMatch = trimmed.match(/^([a-zA-Z0-9_]+):/);
|
|
681
|
+
if (!depMatch) continue;
|
|
682
|
+
const packageName = normalizeTechPackageName(depMatch[1]);
|
|
683
|
+
if (!packageName || packageName === "flutter" || packageName === "sdk") continue;
|
|
684
|
+
packages.add(packageName);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
return packages;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
function parseGoModuleNames(content) {
|
|
691
|
+
const modules = new Set();
|
|
692
|
+
let inRequireBlock = false;
|
|
693
|
+
|
|
694
|
+
for (const rawLine of content.split(/\r?\n/)) {
|
|
695
|
+
const trimmed = rawLine.trim();
|
|
696
|
+
if (!trimmed || trimmed.startsWith("//")) continue;
|
|
697
|
+
|
|
698
|
+
if (trimmed.startsWith("require (")) {
|
|
699
|
+
inRequireBlock = true;
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
if (inRequireBlock && trimmed === ")") {
|
|
703
|
+
inRequireBlock = false;
|
|
704
|
+
continue;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
if (inRequireBlock) {
|
|
708
|
+
const moduleName = normalizeTechPackageName(trimmed.split(/\s+/)[0]);
|
|
709
|
+
if (moduleName) modules.add(moduleName);
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
if (trimmed.startsWith("require ")) {
|
|
714
|
+
const moduleName = normalizeTechPackageName(trimmed.slice("require ".length).trim().split(/\s+/)[0]);
|
|
715
|
+
if (moduleName) modules.add(moduleName);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
return modules;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
function parseRequirementsPackageNames(content) {
|
|
723
|
+
const packages = new Set();
|
|
724
|
+
for (const rawLine of content.split(/\r?\n/)) {
|
|
725
|
+
const withoutComment = rawLine.split("#")[0].trim();
|
|
726
|
+
if (!withoutComment) continue;
|
|
727
|
+
if (withoutComment.startsWith("-")) continue;
|
|
728
|
+
|
|
729
|
+
const cleaned = withoutComment.split(";")[0].trim();
|
|
730
|
+
const match = cleaned.match(/^([A-Za-z0-9_.-]+)/);
|
|
731
|
+
if (!match) continue;
|
|
732
|
+
const packageName = normalizeTechPackageName(match[1]);
|
|
733
|
+
if (packageName) packages.add(packageName);
|
|
734
|
+
}
|
|
735
|
+
return packages;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
function parsePyprojectPackageNames(content) {
|
|
739
|
+
const packages = new Set();
|
|
740
|
+
const sections = parseTomlSections(content);
|
|
741
|
+
|
|
742
|
+
const projectSection = sections.get("project");
|
|
743
|
+
if (projectSection) {
|
|
744
|
+
const projectBody = projectSection.join("\n");
|
|
745
|
+
const dependenciesArrayMatch = projectBody.match(/dependencies\s*=\s*\[([\s\S]*?)\]/m);
|
|
746
|
+
if (dependenciesArrayMatch) {
|
|
747
|
+
const entries = dependenciesArrayMatch[1].match(/"([^"]+)"|'([^']+)'/g) || [];
|
|
748
|
+
for (const entry of entries) {
|
|
749
|
+
const normalizedEntry = normalizeTechPackageName(entry);
|
|
750
|
+
if (!normalizedEntry) continue;
|
|
751
|
+
const packageName = normalizeTechPackageName(normalizedEntry.split(/[<>=!~\s\[]/)[0]);
|
|
752
|
+
if (packageName) packages.add(packageName);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
for (const line of projectSection) {
|
|
757
|
+
const trimmed = line.trim();
|
|
758
|
+
const optionalDepsMatch = trimmed.match(
|
|
759
|
+
/^([A-Za-z0-9_.-]+)\s*=\s*\[\s*("([^"]+)"|'([^']+)')/
|
|
760
|
+
);
|
|
761
|
+
if (!optionalDepsMatch) continue;
|
|
762
|
+
const entry = optionalDepsMatch[3] || optionalDepsMatch[4];
|
|
763
|
+
const packageName = normalizeTechPackageName(String(entry).split(/[<>=!~\s\[]/)[0]);
|
|
764
|
+
if (packageName) packages.add(packageName);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
for (const [sectionName, lines] of sections.entries()) {
|
|
769
|
+
const isPoetryDependencySection =
|
|
770
|
+
sectionName === "tool.poetry.dependencies" ||
|
|
771
|
+
/^tool\.poetry\.group\.[^.]+\.dependencies$/.test(sectionName);
|
|
772
|
+
if (!isPoetryDependencySection) continue;
|
|
773
|
+
|
|
774
|
+
for (const line of lines) {
|
|
775
|
+
const trimmed = line.trim();
|
|
776
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
777
|
+
const depMatch = trimmed.match(/^([A-Za-z0-9_.-]+)\s*=/);
|
|
778
|
+
if (!depMatch) continue;
|
|
779
|
+
const packageName = normalizeTechPackageName(depMatch[1]);
|
|
780
|
+
if (!packageName || packageName === "python") continue;
|
|
781
|
+
packages.add(packageName);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
return packages;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
function parseCargoCrateNames(content) {
|
|
789
|
+
const crates = new Set();
|
|
790
|
+
const sections = parseTomlSections(content);
|
|
791
|
+
|
|
792
|
+
for (const [sectionName, lines] of sections.entries()) {
|
|
793
|
+
const isDependencySection =
|
|
794
|
+
sectionName === "dependencies" ||
|
|
795
|
+
sectionName === "dev-dependencies" ||
|
|
796
|
+
sectionName === "build-dependencies" ||
|
|
797
|
+
sectionName === "workspace.dependencies" ||
|
|
798
|
+
/\.dependencies$/.test(sectionName) ||
|
|
799
|
+
/\.dev-dependencies$/.test(sectionName) ||
|
|
800
|
+
/\.build-dependencies$/.test(sectionName);
|
|
801
|
+
if (!isDependencySection) continue;
|
|
802
|
+
|
|
803
|
+
for (const line of lines) {
|
|
804
|
+
const trimmed = line.trim();
|
|
805
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
806
|
+
const depMatch = trimmed.match(/^([A-Za-z0-9_-]+)\s*=/);
|
|
807
|
+
if (!depMatch) continue;
|
|
808
|
+
const crateName = normalizeTechPackageName(depMatch[1]);
|
|
809
|
+
if (crateName) crates.add(crateName);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
return crates;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
function addFrameworkSignalsFromPackages({ packages, frameworks, signals }) {
|
|
817
|
+
for (const [signal, frameworkLabel] of signals) {
|
|
818
|
+
if (packages.has(signal.toLowerCase())) {
|
|
819
|
+
frameworks.add(frameworkLabel);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
function toSortedArray(values) {
|
|
825
|
+
return [...values].sort((a, b) => a.localeCompare(b));
|
|
826
|
+
}
|
|
827
|
+
|
|
588
828
|
async function collectTechSnapshot(rootDir) {
|
|
589
829
|
const discoveredFiles = [];
|
|
590
830
|
const queue = [rootDir];
|
|
@@ -614,6 +854,12 @@ async function collectTechSnapshot(rootDir) {
|
|
|
614
854
|
|
|
615
855
|
const languageCounts = new Map();
|
|
616
856
|
const topDirs = new Set();
|
|
857
|
+
const packageJsonFiles = [];
|
|
858
|
+
const pubspecFiles = [];
|
|
859
|
+
const goModFiles = [];
|
|
860
|
+
const pyprojectFiles = [];
|
|
861
|
+
const requirementsFiles = [];
|
|
862
|
+
const cargoTomlFiles = [];
|
|
617
863
|
|
|
618
864
|
for (const fullPath of discoveredFiles) {
|
|
619
865
|
const rel = toPosixPath(path.relative(rootDir, fullPath));
|
|
@@ -622,15 +868,31 @@ async function collectTechSnapshot(rootDir) {
|
|
|
622
868
|
|
|
623
869
|
const extension = path.extname(fullPath).toLowerCase();
|
|
624
870
|
const language = TECH_LANGUAGE_BY_EXTENSION.get(extension);
|
|
625
|
-
if (
|
|
626
|
-
|
|
871
|
+
if (language) {
|
|
872
|
+
languageCounts.set(language, (languageCounts.get(language) || 0) + 1);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
const baseName = path.basename(fullPath).toLowerCase();
|
|
876
|
+
if (baseName === "package.json") packageJsonFiles.push(fullPath);
|
|
877
|
+
if (baseName === "pubspec.yaml") pubspecFiles.push(fullPath);
|
|
878
|
+
if (baseName === "go.mod") goModFiles.push(fullPath);
|
|
879
|
+
if (baseName === "pyproject.toml") pyprojectFiles.push(fullPath);
|
|
880
|
+
if (baseName === "cargo.toml") cargoTomlFiles.push(fullPath);
|
|
881
|
+
if (baseName === "requirements.txt" || /^requirements(?:[-_.].+)?\.txt$/.test(baseName)) {
|
|
882
|
+
requirementsFiles.push(fullPath);
|
|
883
|
+
}
|
|
627
884
|
}
|
|
628
885
|
|
|
629
886
|
const fileExists = (name) => existsSync(path.join(rootDir, name));
|
|
630
|
-
const
|
|
887
|
+
const rootPackageJsonPath = path.join(rootDir, "package.json");
|
|
631
888
|
const packageScripts = new Map();
|
|
632
889
|
const frameworks = new Set();
|
|
633
890
|
const lockfiles = [];
|
|
891
|
+
const javascriptPackages = new Set();
|
|
892
|
+
const dartPackages = new Set();
|
|
893
|
+
const goModules = new Set();
|
|
894
|
+
const pythonPackages = new Set();
|
|
895
|
+
const rustCrates = new Set();
|
|
634
896
|
|
|
635
897
|
if (fileExists("bun.lock") || fileExists("bun.lockb")) lockfiles.push("bun");
|
|
636
898
|
if (fileExists("pnpm-lock.yaml")) lockfiles.push("pnpm");
|
|
@@ -641,54 +903,118 @@ async function collectTechSnapshot(rootDir) {
|
|
|
641
903
|
if (fileExists("go.sum")) lockfiles.push("go");
|
|
642
904
|
if (fileExists("pubspec.lock")) lockfiles.push("pub");
|
|
643
905
|
|
|
644
|
-
if (
|
|
645
|
-
if (
|
|
646
|
-
if (
|
|
647
|
-
if (
|
|
906
|
+
if (pubspecFiles.length > 0) frameworks.add("Flutter");
|
|
907
|
+
if (goModFiles.length > 0) frameworks.add("Go Modules");
|
|
908
|
+
if (cargoTomlFiles.length > 0) frameworks.add("Rust Cargo");
|
|
909
|
+
if (requirementsFiles.length > 0 || pyprojectFiles.length > 0) frameworks.add("Python");
|
|
648
910
|
|
|
649
|
-
|
|
911
|
+
for (const packageJsonFile of packageJsonFiles) {
|
|
650
912
|
try {
|
|
651
|
-
const parsed = JSON.parse(await readFile(
|
|
652
|
-
const scripts = parsed.scripts && typeof parsed.scripts === "object" ? parsed.scripts : {};
|
|
653
|
-
for (const [name, command] of Object.entries(scripts)) {
|
|
654
|
-
if (typeof command !== "string") continue;
|
|
655
|
-
packageScripts.set(name, command);
|
|
656
|
-
}
|
|
657
|
-
|
|
913
|
+
const parsed = JSON.parse(await readFile(packageJsonFile, "utf8"));
|
|
658
914
|
const deps = {
|
|
659
915
|
...(parsed.dependencies || {}),
|
|
660
916
|
...(parsed.devDependencies || {}),
|
|
661
|
-
...(parsed.peerDependencies || {})
|
|
917
|
+
...(parsed.peerDependencies || {}),
|
|
918
|
+
...(parsed.optionalDependencies || {})
|
|
662
919
|
};
|
|
663
|
-
const
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
[
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
["tailwindcss", "Tailwind CSS"],
|
|
675
|
-
["prisma", "Prisma"],
|
|
676
|
-
["drizzle-orm", "Drizzle ORM"],
|
|
677
|
-
["mongoose", "Mongoose"],
|
|
678
|
-
["typeorm", "TypeORM"],
|
|
679
|
-
["@playwright/test", "Playwright"],
|
|
680
|
-
["vitest", "Vitest"],
|
|
681
|
-
["jest", "Jest"],
|
|
682
|
-
["cypress", "Cypress"]
|
|
683
|
-
];
|
|
684
|
-
for (const [signal, label] of frameworkSignals) {
|
|
685
|
-
if (depNames.has(signal)) frameworks.add(label);
|
|
920
|
+
for (const depName of Object.keys(deps)) {
|
|
921
|
+
const normalized = normalizeTechPackageName(depName);
|
|
922
|
+
if (normalized) javascriptPackages.add(normalized);
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
if (path.resolve(packageJsonFile) === path.resolve(rootPackageJsonPath)) {
|
|
926
|
+
const scripts = parsed.scripts && typeof parsed.scripts === "object" ? parsed.scripts : {};
|
|
927
|
+
for (const [name, command] of Object.entries(scripts)) {
|
|
928
|
+
if (typeof command !== "string") continue;
|
|
929
|
+
packageScripts.set(name, command);
|
|
930
|
+
}
|
|
686
931
|
}
|
|
687
932
|
} catch {
|
|
688
933
|
// ignore malformed package.json
|
|
689
934
|
}
|
|
690
935
|
}
|
|
691
936
|
|
|
937
|
+
for (const pubspecFile of pubspecFiles) {
|
|
938
|
+
try {
|
|
939
|
+
const content = await readFile(pubspecFile, "utf8");
|
|
940
|
+
for (const packageName of parsePubspecDependencyNames(content)) {
|
|
941
|
+
dartPackages.add(packageName);
|
|
942
|
+
}
|
|
943
|
+
} catch {
|
|
944
|
+
// ignore malformed pubspec.yaml
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
for (const goModFile of goModFiles) {
|
|
949
|
+
try {
|
|
950
|
+
const content = await readFile(goModFile, "utf8");
|
|
951
|
+
for (const moduleName of parseGoModuleNames(content)) {
|
|
952
|
+
goModules.add(moduleName);
|
|
953
|
+
}
|
|
954
|
+
} catch {
|
|
955
|
+
// ignore unreadable go.mod
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
for (const pyprojectFile of pyprojectFiles) {
|
|
960
|
+
try {
|
|
961
|
+
const content = await readFile(pyprojectFile, "utf8");
|
|
962
|
+
for (const packageName of parsePyprojectPackageNames(content)) {
|
|
963
|
+
pythonPackages.add(packageName);
|
|
964
|
+
}
|
|
965
|
+
} catch {
|
|
966
|
+
// ignore malformed pyproject.toml
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
for (const requirementsFile of requirementsFiles) {
|
|
971
|
+
try {
|
|
972
|
+
const content = await readFile(requirementsFile, "utf8");
|
|
973
|
+
for (const packageName of parseRequirementsPackageNames(content)) {
|
|
974
|
+
pythonPackages.add(packageName);
|
|
975
|
+
}
|
|
976
|
+
} catch {
|
|
977
|
+
// ignore unreadable requirements file
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
for (const cargoTomlFile of cargoTomlFiles) {
|
|
982
|
+
try {
|
|
983
|
+
const content = await readFile(cargoTomlFile, "utf8");
|
|
984
|
+
for (const crateName of parseCargoCrateNames(content)) {
|
|
985
|
+
rustCrates.add(crateName);
|
|
986
|
+
}
|
|
987
|
+
} catch {
|
|
988
|
+
// ignore malformed Cargo.toml
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
addFrameworkSignalsFromPackages({
|
|
993
|
+
packages: javascriptPackages,
|
|
994
|
+
frameworks,
|
|
995
|
+
signals: TECH_JS_FRAMEWORK_SIGNALS
|
|
996
|
+
});
|
|
997
|
+
addFrameworkSignalsFromPackages({
|
|
998
|
+
packages: dartPackages,
|
|
999
|
+
frameworks,
|
|
1000
|
+
signals: TECH_DART_FRAMEWORK_SIGNALS
|
|
1001
|
+
});
|
|
1002
|
+
addFrameworkSignalsFromPackages({
|
|
1003
|
+
packages: goModules,
|
|
1004
|
+
frameworks,
|
|
1005
|
+
signals: TECH_GO_FRAMEWORK_SIGNALS
|
|
1006
|
+
});
|
|
1007
|
+
addFrameworkSignalsFromPackages({
|
|
1008
|
+
packages: pythonPackages,
|
|
1009
|
+
frameworks,
|
|
1010
|
+
signals: TECH_PYTHON_FRAMEWORK_SIGNALS
|
|
1011
|
+
});
|
|
1012
|
+
addFrameworkSignalsFromPackages({
|
|
1013
|
+
packages: rustCrates,
|
|
1014
|
+
frameworks,
|
|
1015
|
+
signals: TECH_RUST_FRAMEWORK_SIGNALS
|
|
1016
|
+
});
|
|
1017
|
+
|
|
692
1018
|
const sortedLanguages = [...languageCounts.entries()].sort((a, b) => b[1] - a[1]);
|
|
693
1019
|
const sortedFrameworks = [...frameworks].sort((a, b) => a.localeCompare(b));
|
|
694
1020
|
const sortedTopDirs = [...topDirs].sort((a, b) => a.localeCompare(b)).slice(0, 12);
|
|
@@ -717,10 +1043,33 @@ async function collectTechSnapshot(rootDir) {
|
|
|
717
1043
|
frameworks: sortedFrameworks,
|
|
718
1044
|
lockfiles: sortedLockfiles,
|
|
719
1045
|
topDirs: sortedTopDirs,
|
|
720
|
-
keyScripts
|
|
1046
|
+
keyScripts,
|
|
1047
|
+
packageSignals: {
|
|
1048
|
+
javascript: toSortedArray(javascriptPackages),
|
|
1049
|
+
dart: toSortedArray(dartPackages),
|
|
1050
|
+
go: toSortedArray(goModules),
|
|
1051
|
+
python: toSortedArray(pythonPackages),
|
|
1052
|
+
rust: toSortedArray(rustCrates)
|
|
1053
|
+
}
|
|
721
1054
|
};
|
|
722
1055
|
}
|
|
723
1056
|
|
|
1057
|
+
function appendTechPackageSection(lines, heading, packages) {
|
|
1058
|
+
lines.push(`### ${heading}`);
|
|
1059
|
+
if (!packages || packages.length === 0) {
|
|
1060
|
+
lines.push("- None detected.");
|
|
1061
|
+
} else {
|
|
1062
|
+
const preview = packages.slice(0, TECH_PACKAGE_PREVIEW_LIMIT);
|
|
1063
|
+
for (const packageName of preview) {
|
|
1064
|
+
lines.push(`- \`${packageName}\``);
|
|
1065
|
+
}
|
|
1066
|
+
if (packages.length > TECH_PACKAGE_PREVIEW_LIMIT) {
|
|
1067
|
+
lines.push(`- ... (+${packages.length - TECH_PACKAGE_PREVIEW_LIMIT} more)`);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
lines.push("");
|
|
1071
|
+
}
|
|
1072
|
+
|
|
724
1073
|
function buildTechMd(snapshot) {
|
|
725
1074
|
const lines = [];
|
|
726
1075
|
lines.push("# TECH.md");
|
|
@@ -750,6 +1099,13 @@ function buildTechMd(snapshot) {
|
|
|
750
1099
|
}
|
|
751
1100
|
lines.push("");
|
|
752
1101
|
|
|
1102
|
+
lines.push("## Package Signals");
|
|
1103
|
+
appendTechPackageSection(lines, "JavaScript / TypeScript (package.json)", snapshot.packageSignals.javascript);
|
|
1104
|
+
appendTechPackageSection(lines, "Dart / Flutter (pubspec.yaml)", snapshot.packageSignals.dart);
|
|
1105
|
+
appendTechPackageSection(lines, "Go Modules (go.mod)", snapshot.packageSignals.go);
|
|
1106
|
+
appendTechPackageSection(lines, "Python Packages (requirements / pyproject)", snapshot.packageSignals.python);
|
|
1107
|
+
appendTechPackageSection(lines, "Rust Crates (Cargo.toml)", snapshot.packageSignals.rust);
|
|
1108
|
+
|
|
753
1109
|
lines.push("## Tooling and Lockfiles");
|
|
754
1110
|
if (snapshot.lockfiles.length === 0) {
|
|
755
1111
|
lines.push("- No lockfiles detected.");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cubis/foundry",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.15",
|
|
4
4
|
"description": "Cubis Foundry CLI for workflow-first AI agent environments",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"check": "node bin/cubis.js --help",
|
|
19
19
|
"test:attributes": "node scripts/validate-platform-attributes.mjs",
|
|
20
20
|
"test:attributes:strict": "node scripts/validate-platform-attributes.mjs --strict",
|
|
21
|
+
"test:tech-md": "node scripts/test-tech-md-scanner.mjs",
|
|
21
22
|
"test:smoke": "bash scripts/smoke-workflows.sh",
|
|
22
23
|
"test:all": "npm run test:attributes && npm run test:smoke",
|
|
23
24
|
"test:all:strict": "npm run test:attributes:strict && npm run test:smoke",
|