great-cto 2.1.0 → 2.3.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.
@@ -141,17 +141,20 @@ const RULES = [
141
141
  archetype: "agent-product",
142
142
  score: (d) => {
143
143
  const llms = ["anthropic-sdk", "openai-sdk", "google-ai", "aws-bedrock", "cohere"];
144
+ const llmFrameworks = ["langchain", "llamaindex"]; // RAG-capable LLM frameworks
144
145
  const vdbs = ["pinecone", "weaviate", "chroma", "qdrant"];
145
146
  const agents = ["langgraph", "crewai", "autogen", "mastra", "mcp"];
146
147
  const hasLlm = llms.some((s) => d.stack.includes(s));
148
+ const hasLlmFw = llmFrameworks.some((s) => d.stack.includes(s));
147
149
  const hasVdb = vdbs.some((s) => d.stack.includes(s));
148
150
  const hasAgentFw = agents.some((s) => d.stack.includes(s));
149
151
  let s = 0;
150
- if (hasLlm && hasVdb)
151
- s += 7; // RAG-style agent
152
+ // RAG-style agent: any LLM (raw SDK or framework) + vector DB
153
+ if ((hasLlm || hasLlmFw) && hasVdb)
154
+ s += 7;
152
155
  if (hasAgentFw)
153
156
  s += 6; // explicit agent framework
154
- if (hasLlm && hasAgentFw)
157
+ if ((hasLlm || hasLlmFw) && hasAgentFw)
155
158
  s += 2; // bonus
156
159
  // README mining hint
157
160
  if (d.readmeKeywords.includes("agent") || d.readmeKeywords.includes("ai"))
@@ -212,8 +215,17 @@ const RULES = [
212
215
  if (agents.some((a) => d.stack.includes(a)))
213
216
  s = Math.max(0, s - 2);
214
217
  const vdbs = ["pinecone", "weaviate", "chroma", "qdrant"];
215
- if (vdbs.some((v) => d.stack.includes(v)))
218
+ const hasVdb = vdbs.some((v) => d.stack.includes(v));
219
+ if (hasVdb)
216
220
  s = Math.max(0, s - 2);
221
+ // Strong RAG signal — LangChain/LlamaIndex + VDB is almost certainly
222
+ // an agent-product (RAG-style), not a generic ai-system. The +5 score
223
+ // these frameworks contribute (above) is appropriate when there's no
224
+ // VDB; with a VDB it overcounts. Apply an additional deduction so
225
+ // agent-product wins the tie. See test "LangChain + Pinecone → agent-product".
226
+ const hasLlmFw = ["langchain", "llamaindex"].some((s) => d.stack.includes(s));
227
+ if (hasVdb && hasLlmFw)
228
+ s = Math.max(0, s - 5);
217
229
  return s;
218
230
  },
219
231
  reason: (d) => {
@@ -794,12 +806,173 @@ const RULES = [
794
806
  return "no web/mobile/infra framework detected — looks like a library/SDK";
795
807
  },
796
808
  },
809
+ // ── edtech (education technology — COPPA / FERPA / WCAG-AA child safety) ──
810
+ // Distinct from cms (general content) and healthcare (PHI). Drives age-gate,
811
+ // parental-consent, and accessibility patterns.
812
+ {
813
+ archetype: "edtech",
814
+ score: (d) => {
815
+ let s = 0;
816
+ const lmsLibs = ["canvas-lms", "moodle-api", "schoology-sdk", "blackboard-rest",
817
+ "google-classroom", "khan-academy-cli", "learnosity",
818
+ "kahoot-api", "h5p", "scorm", "lti", "lti-1.3"];
819
+ lmsLibs.forEach((l) => { if (d.stack.includes(l))
820
+ s += 6; });
821
+ // Auth/identity providers commonly used in edtech
822
+ if (d.stack.includes("clever-sdk"))
823
+ s += 6;
824
+ if (d.stack.includes("classlink-sso"))
825
+ s += 4;
826
+ const kws = d.readmeKeywords;
827
+ const eduKeywords = ["student", "classroom", "teacher", "k-12", "k12",
828
+ "lms", "learning management", "grade book", "gradebook",
829
+ "enrollment", "transcript", "pupil", "tutoring", "edtech"];
830
+ const matchedKws = eduKeywords.filter((k) => kws.includes(k));
831
+ if (matchedKws.length >= 2)
832
+ s += 5;
833
+ else if (matchedKws.length === 1)
834
+ s += 2;
835
+ // Strong signal: COPPA / FERPA explicitly mentioned
836
+ if (kws.includes("coppa") || kws.includes("ferpa"))
837
+ s += 6;
838
+ if (kws.includes("parental consent") || kws.includes("age gate"))
839
+ s += 4;
840
+ return s;
841
+ },
842
+ reason: (d) => {
843
+ const kws = d.readmeKeywords;
844
+ const bits = [];
845
+ const lmsLibs = ["canvas-lms", "moodle-api", "schoology-sdk", "google-classroom", "lti", "scorm"];
846
+ lmsLibs.forEach((l) => { if (d.stack.includes(l))
847
+ bits.push(l); });
848
+ if (kws.includes("coppa"))
849
+ bits.push("COPPA mention");
850
+ if (kws.includes("ferpa"))
851
+ bits.push("FERPA mention");
852
+ if (kws.includes("k-12") || kws.includes("k12"))
853
+ bits.push("K-12 keyword");
854
+ if (kws.includes("student"))
855
+ bits.push("student-data keyword");
856
+ return `edtech detected (${bits.join(", ") || "education domain signals"}) — COPPA/FERPA/WCAG-AA child-safety gates required`;
857
+ },
858
+ },
859
+ // ── gov-public (government / civic-tech — FedRAMP / NIST 800-53 / Section 508) ──
860
+ // Severe regulatory burden. Distinct from regulated (which is more EU-focused
861
+ // DORA/NIS2). gov-public targets US federal/state + UK gov.uk patterns.
862
+ {
863
+ archetype: "gov-public",
864
+ score: (d) => {
865
+ let s = 0;
866
+ const govLibs = ["login-gov-sdk", "id-me-sdk", "idme-sdk",
867
+ "usds-design-system", "uswds", "uk-gov-design-system",
868
+ "gov-uk-frontend", "verify-gov-uk",
869
+ "usajobs-sdk", "data-gov", "irs-modernized-efile"];
870
+ govLibs.forEach((l) => { if (d.stack.includes(l))
871
+ s += 6; });
872
+ const kws = d.readmeKeywords;
873
+ const govKeywords = ["fedramp", "fisma", "nist 800-53", "nist-800-53",
874
+ "section 508", "section-508", "ato", "civic tech",
875
+ "government", "municipal", "federal", "agency",
876
+ "gov.uk", "usds", "data.gov", "usa.gov", "irs", "ssa",
877
+ "department of", "stateramp", "cjis"];
878
+ const matchedKws = govKeywords.filter((k) => kws.includes(k));
879
+ if (matchedKws.length >= 2)
880
+ s += 6;
881
+ else if (matchedKws.length === 1)
882
+ s += 3;
883
+ // Very strong signals
884
+ if (kws.includes("fedramp") || kws.includes("fisma"))
885
+ s += 4;
886
+ if (kws.includes("section 508") || kws.includes("section-508"))
887
+ s += 3;
888
+ if (kws.includes("ato"))
889
+ s += 3;
890
+ return s;
891
+ },
892
+ reason: (d) => {
893
+ const kws = d.readmeKeywords;
894
+ const bits = [];
895
+ if (d.stack.includes("login-gov-sdk"))
896
+ bits.push("login.gov");
897
+ if (d.stack.includes("usds-design-system") || d.stack.includes("uswds"))
898
+ bits.push("USWDS");
899
+ if (d.stack.includes("uk-gov-design-system") || d.stack.includes("gov-uk-frontend"))
900
+ bits.push("gov.uk Design System");
901
+ if (kws.includes("fedramp"))
902
+ bits.push("FedRAMP mention");
903
+ if (kws.includes("nist-800-53") || kws.includes("nist 800-53"))
904
+ bits.push("NIST 800-53 mention");
905
+ if (kws.includes("section 508") || kws.includes("section-508"))
906
+ bits.push("Section 508 mention");
907
+ return `gov-public detected (${bits.join(", ") || "government domain signals"}) — FedRAMP/NIST 800-53/Section 508 gates required`;
908
+ },
909
+ },
910
+ // ── insurance (insurtech — NAIC / Solvency II / actuarial / claims fraud) ──
911
+ // Fintech-adjacent but distinct: multi-state filings, anti-discrimination
912
+ // pricing, actuarial model auditability, claims fraud detection.
913
+ {
914
+ archetype: "insurance",
915
+ score: (d) => {
916
+ let s = 0;
917
+ const insuranceLibs = ["acord-standards", "naic-schemas", "drools-rules",
918
+ "solvency2-calc", "openexposure", "ms-actuarial",
919
+ "lloyds-vendor-api", "verisk-sdk", "ccc-one-sdk",
920
+ "guidewire-cloud", "duck-creek", "majesco-sdk",
921
+ "ebix", "aplus-pas"];
922
+ insuranceLibs.forEach((l) => { if (d.stack.includes(l))
923
+ s += 6; });
924
+ const kws = d.readmeKeywords;
925
+ const insuranceKeywords = ["policy", "underwriting", "premium", "claim",
926
+ "actuarial", "reinsurance", "naic", "solvency",
927
+ "broker", "carrier", "mga", "mgu", "tpa",
928
+ "insurance", "insurtech", "insurer", "insured",
929
+ "deductible", "coverage", "bordereau"];
930
+ const matchedKws = insuranceKeywords.filter((k) => kws.includes(k));
931
+ if (matchedKws.length >= 3)
932
+ s += 6;
933
+ else if (matchedKws.length === 2)
934
+ s += 3;
935
+ else if (matchedKws.length === 1)
936
+ s += 1;
937
+ // Very strong signals — NAIC/Solvency/IFRS 17 explicit
938
+ if (kws.includes("naic") || kws.includes("solvency ii") || kws.includes("solvency-ii"))
939
+ s += 5;
940
+ if (kws.includes("ifrs 17") || kws.includes("ifrs-17"))
941
+ s += 4;
942
+ if (kws.includes("insurtech"))
943
+ s += 4;
944
+ // Don't double-score: if also matches commerce/fintech, subtract
945
+ // since insurance is distinct domain (not generic fintech)
946
+ if (d.stack.includes("stripe") && !insuranceLibs.some((l) => d.stack.includes(l))) {
947
+ s = Math.max(0, s - 2);
948
+ }
949
+ return s;
950
+ },
951
+ reason: (d) => {
952
+ const kws = d.readmeKeywords;
953
+ const bits = [];
954
+ const insuranceLibs = ["acord-standards", "naic-schemas", "guidewire-cloud", "duck-creek", "majesco-sdk"];
955
+ insuranceLibs.forEach((l) => { if (d.stack.includes(l))
956
+ bits.push(l); });
957
+ if (kws.includes("naic"))
958
+ bits.push("NAIC mention");
959
+ if (kws.includes("solvency ii") || kws.includes("solvency-ii"))
960
+ bits.push("Solvency II mention");
961
+ if (kws.includes("actuarial"))
962
+ bits.push("actuarial keyword");
963
+ if (kws.includes("underwriting"))
964
+ bits.push("underwriting keyword");
965
+ if (kws.includes("insurtech"))
966
+ bits.push("insurtech keyword");
967
+ return `insurance detected (${bits.join(", ") || "insurance domain signals"}) — NAIC/Solvency II/actuarial-audit gates required`;
968
+ },
969
+ },
797
970
  ];
798
971
  // Tie-break priority — when two rules score equally, prefer the one
799
972
  // higher in this list (more specific / domain-bound first).
800
973
  const TIE_BREAK_PRIORITY = [
801
974
  "browser-extension", "iot-embedded", "web3", "game",
802
- "agent-product", "fintech", "healthcare", "marketplace",
975
+ "agent-product", "fintech", "insurance", "healthcare", "edtech", "gov-public", "marketplace",
803
976
  "mlops", "streaming",
804
977
  "commerce", "enterprise-saas", "ai-system", "devtools",
805
978
  "data-platform", "cms", "infra", "mobile-app",
@@ -944,6 +1117,41 @@ export function suggestCompliance(d, archetype) {
944
1117
  c.add("gdpr");
945
1118
  c.add("dsa-eu");
946
1119
  }
1120
+ if (archetype === "edtech") {
1121
+ c.add("coppa");
1122
+ c.add("ferpa");
1123
+ c.add("gdpr-k");
1124
+ c.add("wcag-2.2-aa");
1125
+ c.add("section-508");
1126
+ // State student-privacy laws
1127
+ c.add("sopipa-ca");
1128
+ }
1129
+ if (archetype === "gov-public") {
1130
+ c.add("fedramp");
1131
+ c.add("nist-800-53");
1132
+ c.add("fisma");
1133
+ c.add("section-508");
1134
+ c.add("pia");
1135
+ // CJIS only if law-enforcement keywords present
1136
+ const kws = d.readmeKeywords;
1137
+ if (kws.includes("cjis") || kws.includes("law enforcement") || kws.includes("criminal justice")) {
1138
+ c.add("cjis");
1139
+ }
1140
+ // StateRAMP if state-level
1141
+ if (kws.includes("stateramp") || kws.includes("state government"))
1142
+ c.add("stateramp");
1143
+ c.add("ato"); // Authority to Operate
1144
+ }
1145
+ if (archetype === "insurance") {
1146
+ c.add("naic");
1147
+ c.add("solvency-ii");
1148
+ c.add("ifrs-17");
1149
+ c.add("gdpr");
1150
+ c.add("ccpa");
1151
+ c.add("anti-discrimination-pricing");
1152
+ c.add("actuarial-asops");
1153
+ c.add("state-doi"); // Department of Insurance per US state
1154
+ }
947
1155
  // ── stack-derived (cross-archetype) ──────────────
948
1156
  if (d.stack.includes("stripe") || d.stack.includes("braintree") ||
949
1157
  d.stack.includes("adyen") || d.stack.includes("paddle")) {
package/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  // Entry point: loads the compiled dist/main.js.
3
3
  // For local dev, run: `npm run build` first, then `node index.mjs`.
4
- import { fileURLToPath } from "node:url";
4
+ import { fileURLToPath, pathToFileURL } from "node:url";
5
5
  import { dirname, join } from "node:path";
6
6
  import { existsSync } from "node:fs";
7
7
 
@@ -14,4 +14,7 @@ if (!existsSync(compiled)) {
14
14
  process.exit(1);
15
15
  }
16
16
 
17
- await import(compiled);
17
+ // Convert path to file:// URL — required by Node's ESM loader on Windows
18
+ // (absolute paths like "D:\\..." are not accepted as bare strings).
19
+ // Works equivalently on macOS / Linux.
20
+ await import(pathToFileURL(compiled).href);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "great-cto",
3
- "version": "2.1.0",
3
+ "version": "2.3.0",
4
4
  "description": "One command install for the great_cto Claude Code plugin. Auto-detects your stack, picks the right archetype, bootstraps PROJECT.md.",
5
5
  "keywords": [
6
6
  "claude-code",