great-cto 1.0.169 → 1.1.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/archetypes.js +368 -15
- package/dist/detect.js +167 -2
- package/package.json +16 -3
package/dist/archetypes.js
CHANGED
|
@@ -51,9 +51,9 @@ const RULES = [
|
|
|
51
51
|
if (d.stack.includes("devtools-api"))
|
|
52
52
|
s += 7;
|
|
53
53
|
if (d.stack.includes("openapi-spec"))
|
|
54
|
-
s +=
|
|
54
|
+
s += 8;
|
|
55
55
|
if (d.stack.includes("graphql-schema"))
|
|
56
|
-
s +=
|
|
56
|
+
s += 6;
|
|
57
57
|
if (d.stack.includes("multi-sdk"))
|
|
58
58
|
s += 4;
|
|
59
59
|
if (d.stack.includes("stainless"))
|
|
@@ -183,25 +183,30 @@ const RULES = [
|
|
|
183
183
|
score: (d) => {
|
|
184
184
|
let s = 0;
|
|
185
185
|
if (d.stack.includes("anthropic-sdk"))
|
|
186
|
-
s +=
|
|
186
|
+
s += 6;
|
|
187
187
|
if (d.stack.includes("openai-sdk"))
|
|
188
|
-
s +=
|
|
188
|
+
s += 5;
|
|
189
189
|
if (d.stack.includes("google-ai"))
|
|
190
|
-
s +=
|
|
190
|
+
s += 5;
|
|
191
191
|
if (d.stack.includes("aws-bedrock"))
|
|
192
|
-
s +=
|
|
192
|
+
s += 5;
|
|
193
193
|
if (d.stack.includes("cohere"))
|
|
194
|
-
s +=
|
|
194
|
+
s += 4;
|
|
195
195
|
if (d.stack.includes("replicate"))
|
|
196
|
-
s +=
|
|
196
|
+
s += 4;
|
|
197
197
|
if (d.stack.includes("langchain"))
|
|
198
|
-
s +=
|
|
198
|
+
s += 5;
|
|
199
199
|
if (d.stack.includes("llamaindex"))
|
|
200
|
-
s +=
|
|
200
|
+
s += 5;
|
|
201
201
|
if (d.stack.includes("vercel-ai-sdk"))
|
|
202
|
-
s +=
|
|
202
|
+
s += 5;
|
|
203
203
|
if (d.stack.includes("ml"))
|
|
204
204
|
s += 2;
|
|
205
|
+
// Combo bonus: using multiple AI providers/frameworks together
|
|
206
|
+
const aiCount = ["anthropic-sdk", "openai-sdk", "google-ai", "aws-bedrock", "cohere", "replicate", "langchain", "llamaindex", "vercel-ai-sdk"]
|
|
207
|
+
.filter(x => d.stack.includes(x)).length;
|
|
208
|
+
if (aiCount >= 2)
|
|
209
|
+
s += 3;
|
|
205
210
|
// Don't double-score if already an agent-product
|
|
206
211
|
const agents = ["langgraph", "crewai", "autogen", "mastra", "mcp"];
|
|
207
212
|
if (agents.some((a) => d.stack.includes(a)))
|
|
@@ -264,6 +269,241 @@ const RULES = [
|
|
|
264
269
|
return `fintech integration: ${bits.join(", ")} — SOX, PCI, KYC/AML compliance gates`;
|
|
265
270
|
},
|
|
266
271
|
},
|
|
272
|
+
// ── marketplace (two-sided platform — Stripe Connect / KYC) ────
|
|
273
|
+
// Stronger than commerce when payouts to sellers + KYC providers detected
|
|
274
|
+
{
|
|
275
|
+
archetype: "marketplace",
|
|
276
|
+
score: (d) => {
|
|
277
|
+
let s = 0;
|
|
278
|
+
if (d.stack.includes("stripe-connect"))
|
|
279
|
+
s += 9;
|
|
280
|
+
if (d.stack.includes("adyen-marketpay"))
|
|
281
|
+
s += 9;
|
|
282
|
+
if (d.stack.includes("persona"))
|
|
283
|
+
s += 5;
|
|
284
|
+
if (d.stack.includes("onfido"))
|
|
285
|
+
s += 5;
|
|
286
|
+
if (d.stack.includes("sumsub"))
|
|
287
|
+
s += 5;
|
|
288
|
+
if (d.readmeKeywords.includes("marketplace") || d.readmeKeywords.includes("two-sided"))
|
|
289
|
+
s += 4;
|
|
290
|
+
if (d.readmeKeywords.includes("seller") && d.readmeKeywords.includes("buyer"))
|
|
291
|
+
s += 3;
|
|
292
|
+
// Don't score if pure single-merchant commerce
|
|
293
|
+
if (s > 0 && d.stack.includes("stripe") && !d.stack.includes("stripe-connect"))
|
|
294
|
+
s += 1;
|
|
295
|
+
return s;
|
|
296
|
+
},
|
|
297
|
+
reason: (d) => {
|
|
298
|
+
const bits = [];
|
|
299
|
+
if (d.stack.includes("stripe-connect"))
|
|
300
|
+
bits.push("Stripe Connect");
|
|
301
|
+
if (d.stack.includes("adyen-marketpay"))
|
|
302
|
+
bits.push("Adyen MarketPay");
|
|
303
|
+
if (d.stack.includes("persona") || d.stack.includes("onfido") || d.stack.includes("sumsub"))
|
|
304
|
+
bits.push("KYC vendor");
|
|
305
|
+
return `marketplace / two-sided platform (${bits.join(", ")}) — multi-party payouts + seller KYC + 1099-K + DSA`;
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
// ── enterprise-saas (B2B multi-tenant, SSO/SAML/SCIM, audit) ──
|
|
309
|
+
{
|
|
310
|
+
archetype: "enterprise-saas",
|
|
311
|
+
score: (d) => {
|
|
312
|
+
let s = 0;
|
|
313
|
+
if (d.stack.includes("workos"))
|
|
314
|
+
s += 8; // SSO/SCIM-as-a-service
|
|
315
|
+
if (d.stack.includes("auth0"))
|
|
316
|
+
s += 6;
|
|
317
|
+
if (d.stack.includes("okta"))
|
|
318
|
+
s += 6;
|
|
319
|
+
if (d.stack.includes("clerk"))
|
|
320
|
+
s += 4;
|
|
321
|
+
if (d.stack.includes("samlify"))
|
|
322
|
+
s += 6;
|
|
323
|
+
if (d.stack.includes("passport-saml"))
|
|
324
|
+
s += 6;
|
|
325
|
+
if (d.stack.includes("scim"))
|
|
326
|
+
s += 5;
|
|
327
|
+
if (d.readmeKeywords.includes("multi-tenant") || d.readmeKeywords.includes("multitenant"))
|
|
328
|
+
s += 4;
|
|
329
|
+
if (d.readmeKeywords.includes("enterprise") || d.readmeKeywords.includes("b2b"))
|
|
330
|
+
s += 3;
|
|
331
|
+
if (d.readmeKeywords.includes("sso") || d.readmeKeywords.includes("saml"))
|
|
332
|
+
s += 3;
|
|
333
|
+
// Stripe billing + multi-tenant signals = SaaS
|
|
334
|
+
if (s > 0 && d.stack.includes("stripe"))
|
|
335
|
+
s += 1;
|
|
336
|
+
return s;
|
|
337
|
+
},
|
|
338
|
+
reason: (d) => {
|
|
339
|
+
const bits = [];
|
|
340
|
+
if (d.stack.includes("workos"))
|
|
341
|
+
bits.push("WorkOS");
|
|
342
|
+
if (d.stack.includes("auth0"))
|
|
343
|
+
bits.push("Auth0");
|
|
344
|
+
if (d.stack.includes("okta"))
|
|
345
|
+
bits.push("Okta");
|
|
346
|
+
if (d.stack.includes("samlify") || d.stack.includes("passport-saml"))
|
|
347
|
+
bits.push("SAML lib");
|
|
348
|
+
if (d.stack.includes("scim"))
|
|
349
|
+
bits.push("SCIM");
|
|
350
|
+
return `enterprise B2B SaaS (${bits.join(", ")}) — multi-tenant isolation + SSO + audit log + SOC2 mandatory`;
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
// ── mlops (model training & lifecycle, distinct from inference) ─
|
|
354
|
+
{
|
|
355
|
+
archetype: "mlops",
|
|
356
|
+
score: (d) => {
|
|
357
|
+
let s = 0;
|
|
358
|
+
if (d.stack.includes("mlflow"))
|
|
359
|
+
s += 7;
|
|
360
|
+
if (d.stack.includes("wandb"))
|
|
361
|
+
s += 6;
|
|
362
|
+
if (d.stack.includes("dvc"))
|
|
363
|
+
s += 6;
|
|
364
|
+
if (d.stack.includes("kubeflow"))
|
|
365
|
+
s += 7;
|
|
366
|
+
if (d.stack.includes("bentoml"))
|
|
367
|
+
s += 5;
|
|
368
|
+
if (d.stack.includes("seldon"))
|
|
369
|
+
s += 5;
|
|
370
|
+
if (d.stack.includes("kserve"))
|
|
371
|
+
s += 5;
|
|
372
|
+
if (d.stack.includes("sagemaker"))
|
|
373
|
+
s += 5;
|
|
374
|
+
if (d.stack.includes("vertex-ai"))
|
|
375
|
+
s += 5;
|
|
376
|
+
if (d.stack.includes("ray"))
|
|
377
|
+
s += 4;
|
|
378
|
+
if (d.stack.includes("torch") || d.stack.includes("tensorflow"))
|
|
379
|
+
s += 2;
|
|
380
|
+
if (d.readmeKeywords.includes("training") && d.readmeKeywords.includes("model"))
|
|
381
|
+
s += 3;
|
|
382
|
+
// De-prioritize if pure inference (LLM API only)
|
|
383
|
+
if (s > 0 && d.stack.includes("anthropic-sdk") && !d.stack.includes("torch") && !d.stack.includes("mlflow"))
|
|
384
|
+
s -= 2;
|
|
385
|
+
return s;
|
|
386
|
+
},
|
|
387
|
+
reason: (d) => {
|
|
388
|
+
const bits = [];
|
|
389
|
+
if (d.stack.includes("mlflow"))
|
|
390
|
+
bits.push("MLflow");
|
|
391
|
+
if (d.stack.includes("wandb"))
|
|
392
|
+
bits.push("W&B");
|
|
393
|
+
if (d.stack.includes("dvc"))
|
|
394
|
+
bits.push("DVC");
|
|
395
|
+
if (d.stack.includes("kubeflow"))
|
|
396
|
+
bits.push("Kubeflow");
|
|
397
|
+
if (d.stack.includes("bentoml"))
|
|
398
|
+
bits.push("BentoML");
|
|
399
|
+
if (d.stack.includes("torch"))
|
|
400
|
+
bits.push("PyTorch");
|
|
401
|
+
if (d.stack.includes("tensorflow"))
|
|
402
|
+
bits.push("TensorFlow");
|
|
403
|
+
return `MLOps stack detected (${bits.join(", ")}) — dataset versioning + drift detection + model registry + EU AI Act high-risk gates`;
|
|
404
|
+
},
|
|
405
|
+
},
|
|
406
|
+
// ── streaming (event-driven / Kafka / Flink — distinct from batch data-platform) ─
|
|
407
|
+
{
|
|
408
|
+
archetype: "streaming",
|
|
409
|
+
score: (d) => {
|
|
410
|
+
let s = 0;
|
|
411
|
+
if (d.stack.includes("kafkajs"))
|
|
412
|
+
s += 7;
|
|
413
|
+
if (d.stack.includes("kafka-node"))
|
|
414
|
+
s += 6;
|
|
415
|
+
if (d.stack.includes("rdkafka"))
|
|
416
|
+
s += 7;
|
|
417
|
+
if (d.stack.includes("kinesis"))
|
|
418
|
+
s += 6;
|
|
419
|
+
if (d.stack.includes("pulsar"))
|
|
420
|
+
s += 6;
|
|
421
|
+
if (d.stack.includes("flink"))
|
|
422
|
+
s += 7;
|
|
423
|
+
if (d.stack.includes("beam"))
|
|
424
|
+
s += 6;
|
|
425
|
+
if (d.stack.includes("debezium"))
|
|
426
|
+
s += 7;
|
|
427
|
+
if (d.stack.includes("nats"))
|
|
428
|
+
s += 5;
|
|
429
|
+
if (d.stack.includes("rabbitmq"))
|
|
430
|
+
s += 4;
|
|
431
|
+
if (d.readmeKeywords.includes("streaming") || d.readmeKeywords.includes("event-driven"))
|
|
432
|
+
s += 2;
|
|
433
|
+
if (d.readmeKeywords.includes("cdc") || d.readmeKeywords.includes("real-time"))
|
|
434
|
+
s += 2;
|
|
435
|
+
return s;
|
|
436
|
+
},
|
|
437
|
+
reason: (d) => {
|
|
438
|
+
const bits = [];
|
|
439
|
+
if (d.stack.includes("kafkajs") || d.stack.includes("rdkafka") || d.stack.includes("kafka-node"))
|
|
440
|
+
bits.push("Kafka");
|
|
441
|
+
if (d.stack.includes("kinesis"))
|
|
442
|
+
bits.push("Kinesis");
|
|
443
|
+
if (d.stack.includes("pulsar"))
|
|
444
|
+
bits.push("Pulsar");
|
|
445
|
+
if (d.stack.includes("flink"))
|
|
446
|
+
bits.push("Flink");
|
|
447
|
+
if (d.stack.includes("beam"))
|
|
448
|
+
bits.push("Beam");
|
|
449
|
+
if (d.stack.includes("debezium"))
|
|
450
|
+
bits.push("Debezium CDC");
|
|
451
|
+
return `streaming / event-driven stack (${bits.join(", ")}) — exactly-once + backpressure + DLQ + schema evolution gates`;
|
|
452
|
+
},
|
|
453
|
+
},
|
|
454
|
+
// ── cms / content platform (headless CMS, publishing, SEO) ────
|
|
455
|
+
{
|
|
456
|
+
archetype: "cms",
|
|
457
|
+
score: (d) => {
|
|
458
|
+
let s = 0;
|
|
459
|
+
if (d.stack.includes("sanity"))
|
|
460
|
+
s += 9;
|
|
461
|
+
if (d.stack.includes("contentful"))
|
|
462
|
+
s += 9;
|
|
463
|
+
if (d.stack.includes("strapi"))
|
|
464
|
+
s += 9;
|
|
465
|
+
if (d.stack.includes("payload"))
|
|
466
|
+
s += 8;
|
|
467
|
+
if (d.stack.includes("ghost"))
|
|
468
|
+
s += 8;
|
|
469
|
+
if (d.stack.includes("gatsby"))
|
|
470
|
+
s += 5;
|
|
471
|
+
if (d.stack.includes("eleventy"))
|
|
472
|
+
s += 5;
|
|
473
|
+
if (d.stack.includes("hugo"))
|
|
474
|
+
s += 5;
|
|
475
|
+
if (d.stack.includes("astro") && d.readmeKeywords.includes("blog"))
|
|
476
|
+
s += 4;
|
|
477
|
+
if (d.stack.includes("next.js") && d.readmeKeywords.includes("blog"))
|
|
478
|
+
s += 3;
|
|
479
|
+
if (d.readmeKeywords.includes("cms") || d.readmeKeywords.includes("publishing") || d.readmeKeywords.includes("blog"))
|
|
480
|
+
s += 2;
|
|
481
|
+
// De-prioritize if it's clearly an enterprise SaaS app
|
|
482
|
+
if (s > 0 && (d.stack.includes("workos") || d.stack.includes("samlify")))
|
|
483
|
+
s -= 3;
|
|
484
|
+
return s;
|
|
485
|
+
},
|
|
486
|
+
reason: (d) => {
|
|
487
|
+
const bits = [];
|
|
488
|
+
if (d.stack.includes("sanity"))
|
|
489
|
+
bits.push("Sanity");
|
|
490
|
+
if (d.stack.includes("contentful"))
|
|
491
|
+
bits.push("Contentful");
|
|
492
|
+
if (d.stack.includes("strapi"))
|
|
493
|
+
bits.push("Strapi");
|
|
494
|
+
if (d.stack.includes("payload"))
|
|
495
|
+
bits.push("Payload");
|
|
496
|
+
if (d.stack.includes("ghost"))
|
|
497
|
+
bits.push("Ghost");
|
|
498
|
+
if (d.stack.includes("gatsby"))
|
|
499
|
+
bits.push("Gatsby");
|
|
500
|
+
if (d.stack.includes("eleventy"))
|
|
501
|
+
bits.push("Eleventy");
|
|
502
|
+
if (d.stack.includes("hugo"))
|
|
503
|
+
bits.push("Hugo");
|
|
504
|
+
return `CMS / publishing stack (${bits.join(", ")}) — schema.org + Core Web Vitals + DMCA + UGC moderation gates`;
|
|
505
|
+
},
|
|
506
|
+
},
|
|
267
507
|
// ── healthcare (FHIR/HL7/PHI) ───────────────────
|
|
268
508
|
{
|
|
269
509
|
archetype: "healthcare",
|
|
@@ -286,6 +526,72 @@ const RULES = [
|
|
|
286
526
|
return `healthcare data tooling: ${bits.join(", ")} — HIPAA/PHI handling gates required`;
|
|
287
527
|
},
|
|
288
528
|
},
|
|
529
|
+
// ── regulated (compliance-first: DORA/NIS2/SOX/FedRAMP — not fintech/healthcare) ──
|
|
530
|
+
{
|
|
531
|
+
archetype: "regulated",
|
|
532
|
+
score: (d) => {
|
|
533
|
+
let s = 0;
|
|
534
|
+
// Compliance automation SaaS installed → near-certain regulated industry
|
|
535
|
+
if (d.stack.includes("compliance-automation"))
|
|
536
|
+
s += 10;
|
|
537
|
+
// Compliance documentation structure
|
|
538
|
+
if (d.stack.includes("compliance-docs"))
|
|
539
|
+
s += 7;
|
|
540
|
+
// Audit log package
|
|
541
|
+
if (d.stack.includes("audit-log"))
|
|
542
|
+
s += 5;
|
|
543
|
+
// README regulatory signals
|
|
544
|
+
const kws = d.readmeKeywords;
|
|
545
|
+
if (kws.includes("regulated"))
|
|
546
|
+
s += 5;
|
|
547
|
+
// FedRAMP / FISMA / CMMC → US federal regulated
|
|
548
|
+
const fedSignals = ["fedramp", "fisma", "cmmc"];
|
|
549
|
+
if (fedSignals.some((k) => kws.includes(k)))
|
|
550
|
+
s += 8;
|
|
551
|
+
// DORA ICT / NIS2 → EU regulated
|
|
552
|
+
if (kws.includes("dora ict") || kws.includes("nis2"))
|
|
553
|
+
s += 6;
|
|
554
|
+
// SOX compliance (non-fintech context)
|
|
555
|
+
if (kws.includes("sox compliance") || kws.includes("sarbanes")) {
|
|
556
|
+
if (!d.stack.includes("plaid") && !d.stack.includes("fintech"))
|
|
557
|
+
s += 5;
|
|
558
|
+
}
|
|
559
|
+
// Generic compliance/audit signals without stronger domain archetype
|
|
560
|
+
if (kws.includes("compliance automation") || kws.includes("audit trail"))
|
|
561
|
+
s += 4;
|
|
562
|
+
if (kws.includes("iso 27001") || kws.includes("soc 2 type"))
|
|
563
|
+
s += 3;
|
|
564
|
+
// Hard exclude: fintech and healthcare have their own dedicated archetypes
|
|
565
|
+
if (d.stack.includes("plaid") || d.stack.includes("wise") || d.stack.includes("fhir"))
|
|
566
|
+
s = 0;
|
|
567
|
+
return s;
|
|
568
|
+
},
|
|
569
|
+
reason: (d) => {
|
|
570
|
+
const bits = [];
|
|
571
|
+
if (d.stack.includes("compliance-automation"))
|
|
572
|
+
bits.push("compliance automation (Vanta/Drata/Secureframe)");
|
|
573
|
+
if (d.stack.includes("compliance-docs"))
|
|
574
|
+
bits.push("ISMS/risk-register/compliance docs");
|
|
575
|
+
if (d.stack.includes("audit-log"))
|
|
576
|
+
bits.push("audit log package");
|
|
577
|
+
const kws = d.readmeKeywords;
|
|
578
|
+
if (kws.includes("regulated"))
|
|
579
|
+
bits.push("regulated-industry README");
|
|
580
|
+
if (kws.includes("fedramp"))
|
|
581
|
+
bits.push("FedRAMP");
|
|
582
|
+
if (kws.includes("fisma"))
|
|
583
|
+
bits.push("FISMA");
|
|
584
|
+
if (kws.includes("cmmc"))
|
|
585
|
+
bits.push("CMMC");
|
|
586
|
+
if (kws.includes("dora ict"))
|
|
587
|
+
bits.push("DORA ICT");
|
|
588
|
+
if (kws.includes("nis2"))
|
|
589
|
+
bits.push("NIS2");
|
|
590
|
+
if (kws.includes("sox compliance") || kws.includes("sarbanes"))
|
|
591
|
+
bits.push("SOX");
|
|
592
|
+
return `regulated-industry signals (${bits.join(", ")}) — DORA/NIS2/SOX/FedRAMP compliance gates required`;
|
|
593
|
+
},
|
|
594
|
+
},
|
|
289
595
|
// ── cli-tool (explicit CLI: bin field + cli entry) ─
|
|
290
596
|
{
|
|
291
597
|
archetype: "cli-tool",
|
|
@@ -294,6 +600,9 @@ const RULES = [
|
|
|
294
600
|
// Explicit cli marker from detect.ts (bin field present)
|
|
295
601
|
if (d.stack.includes("cli"))
|
|
296
602
|
s += 6;
|
|
603
|
+
// Python CLI via [project.scripts] / console_scripts entry
|
|
604
|
+
if (d.stack.includes("python-cli"))
|
|
605
|
+
s += 7;
|
|
297
606
|
if (d.codeStructure.hasCliEntry)
|
|
298
607
|
s += 2;
|
|
299
608
|
if (d.readmeKeywords.includes("cli"))
|
|
@@ -490,10 +799,11 @@ const RULES = [
|
|
|
490
799
|
// higher in this list (more specific / domain-bound first).
|
|
491
800
|
const TIE_BREAK_PRIORITY = [
|
|
492
801
|
"browser-extension", "iot-embedded", "web3", "game",
|
|
493
|
-
"agent-product", "fintech", "healthcare",
|
|
494
|
-
"
|
|
495
|
-
"
|
|
496
|
-
"
|
|
802
|
+
"agent-product", "fintech", "healthcare", "marketplace",
|
|
803
|
+
"mlops", "streaming",
|
|
804
|
+
"commerce", "enterprise-saas", "ai-system", "devtools",
|
|
805
|
+
"data-platform", "cms", "infra", "mobile-app",
|
|
806
|
+
"regulated", "cli-tool", "web-service", "library", "greenfield",
|
|
497
807
|
];
|
|
498
808
|
function priorityIndex(a) {
|
|
499
809
|
const i = TIE_BREAK_PRIORITY.indexOf(a);
|
|
@@ -588,9 +898,52 @@ export function suggestCompliance(d, archetype) {
|
|
|
588
898
|
c.add("soc2-type-2");
|
|
589
899
|
c.add("gdpr");
|
|
590
900
|
}
|
|
901
|
+
if (archetype === "regulated") {
|
|
902
|
+
c.add("iso27001");
|
|
903
|
+
c.add("gdpr");
|
|
904
|
+
c.add("compliance-required");
|
|
905
|
+
// FedRAMP/CMMC if US federal signals present
|
|
906
|
+
const kws = d.readmeKeywords;
|
|
907
|
+
if (kws.includes("fedramp") || kws.includes("fisma") || kws.includes("cmmc"))
|
|
908
|
+
c.add("fedramp");
|
|
909
|
+
if (kws.includes("dora ict") || kws.includes("nis2")) {
|
|
910
|
+
c.add("dora-ict");
|
|
911
|
+
c.add("nis2");
|
|
912
|
+
}
|
|
913
|
+
if (kws.includes("sox compliance") || kws.includes("sarbanes"))
|
|
914
|
+
c.add("sox");
|
|
915
|
+
}
|
|
591
916
|
if (archetype === "web-service")
|
|
592
917
|
c.add("gdpr"); // baseline for user data
|
|
593
918
|
if (archetype === "cli-tool") { /* CLI tools usually don't have compliance load */ }
|
|
919
|
+
if (archetype === "marketplace") {
|
|
920
|
+
c.add("pci-dss");
|
|
921
|
+
c.add("kyc-aml");
|
|
922
|
+
c.add("gdpr");
|
|
923
|
+
c.add("dsa-eu");
|
|
924
|
+
c.add("p2b-eu");
|
|
925
|
+
c.add("1099-k");
|
|
926
|
+
}
|
|
927
|
+
if (archetype === "enterprise-saas") {
|
|
928
|
+
c.add("soc2-type-2");
|
|
929
|
+
c.add("iso27001");
|
|
930
|
+
c.add("gdpr");
|
|
931
|
+
c.add("ccpa");
|
|
932
|
+
}
|
|
933
|
+
if (archetype === "mlops") {
|
|
934
|
+
c.add("eu-ai-act");
|
|
935
|
+
c.add("nist-ai-rmf");
|
|
936
|
+
c.add("iso42001");
|
|
937
|
+
}
|
|
938
|
+
if (archetype === "streaming") {
|
|
939
|
+
c.add("gdpr"); // event retention rules
|
|
940
|
+
}
|
|
941
|
+
if (archetype === "cms") {
|
|
942
|
+
c.add("dmca");
|
|
943
|
+
c.add("wcag-2.2");
|
|
944
|
+
c.add("gdpr");
|
|
945
|
+
c.add("dsa-eu");
|
|
946
|
+
}
|
|
594
947
|
// ── stack-derived (cross-archetype) ──────────────
|
|
595
948
|
if (d.stack.includes("stripe") || d.stack.includes("braintree") ||
|
|
596
949
|
d.stack.includes("adyen") || d.stack.includes("paddle")) {
|
package/dist/detect.js
CHANGED
|
@@ -219,6 +219,89 @@ export function detect(dir) {
|
|
|
219
219
|
if (has("@workos-inc/node")) {
|
|
220
220
|
stack.add("workos");
|
|
221
221
|
}
|
|
222
|
+
if (has("auth0") || has("@auth0/nextjs-auth0") || has("@auth0/auth0-react"))
|
|
223
|
+
stack.add("auth0");
|
|
224
|
+
if (has("@okta/okta-auth-js") || has("@okta/oidc-middleware"))
|
|
225
|
+
stack.add("okta");
|
|
226
|
+
if (has("samlify"))
|
|
227
|
+
stack.add("samlify");
|
|
228
|
+
if (has("passport-saml") || has("@node-saml/passport-saml"))
|
|
229
|
+
stack.add("passport-saml");
|
|
230
|
+
if (has("@scim2/core") || has("scim2") || has("scim-patch"))
|
|
231
|
+
stack.add("scim");
|
|
232
|
+
// Compliance automation (regulated industry — vanta/drata/secureframe are decisive signals)
|
|
233
|
+
if (has("vanta") || has("@vanta/agent")) {
|
|
234
|
+
stack.add("compliance-automation");
|
|
235
|
+
sig("regulated", "vanta");
|
|
236
|
+
}
|
|
237
|
+
if (has("drata-cli") || has("@drata/sdk")) {
|
|
238
|
+
stack.add("compliance-automation");
|
|
239
|
+
sig("regulated", "drata");
|
|
240
|
+
}
|
|
241
|
+
if (has("secureframe") || has("@secureframe/node")) {
|
|
242
|
+
stack.add("compliance-automation");
|
|
243
|
+
sig("regulated", "secureframe");
|
|
244
|
+
}
|
|
245
|
+
if (has("tugboat-logic")) {
|
|
246
|
+
stack.add("compliance-automation");
|
|
247
|
+
sig("regulated", "tugboat-logic");
|
|
248
|
+
}
|
|
249
|
+
// Audit log packages
|
|
250
|
+
if (has("audit-log") || has("node-audit-logger") || has("winston-audit")) {
|
|
251
|
+
stack.add("audit-log");
|
|
252
|
+
sig("regulated", "audit-log");
|
|
253
|
+
}
|
|
254
|
+
// Marketplace / KYC providers
|
|
255
|
+
if (has("@stripe/connect-iframe-loader") || (has("stripe") && (pkg.name?.includes("market") || pkg.name?.includes("connect")))) {
|
|
256
|
+
stack.add("stripe-connect");
|
|
257
|
+
sig("marketplace", "stripe-connect");
|
|
258
|
+
}
|
|
259
|
+
if (has("@adyen/marketpay"))
|
|
260
|
+
stack.add("adyen-marketpay");
|
|
261
|
+
if (has("persona-react") || has("persona-sdk"))
|
|
262
|
+
stack.add("persona");
|
|
263
|
+
if (has("@onfido/api") || has("onfido-sdk-ui"))
|
|
264
|
+
stack.add("onfido");
|
|
265
|
+
if (has("@sumsub/websdk-react"))
|
|
266
|
+
stack.add("sumsub");
|
|
267
|
+
// CMS / publishing
|
|
268
|
+
if (has("@sanity/client") || has("next-sanity") || has("sanity"))
|
|
269
|
+
stack.add("sanity");
|
|
270
|
+
if (has("contentful") || has("@contentful/rich-text-react-renderer"))
|
|
271
|
+
stack.add("contentful");
|
|
272
|
+
if (has("strapi") || has("@strapi/strapi"))
|
|
273
|
+
stack.add("strapi");
|
|
274
|
+
if (has("payload") || has("@payloadcms/payload"))
|
|
275
|
+
stack.add("payload");
|
|
276
|
+
if (has("@tryghost/content-api") || has("ghost"))
|
|
277
|
+
stack.add("ghost");
|
|
278
|
+
if (has("gatsby"))
|
|
279
|
+
stack.add("gatsby");
|
|
280
|
+
if (has("@11ty/eleventy"))
|
|
281
|
+
stack.add("eleventy");
|
|
282
|
+
// Streaming / messaging
|
|
283
|
+
if (has("kafkajs") || has("@confluentinc/kafka-javascript"))
|
|
284
|
+
stack.add("kafkajs");
|
|
285
|
+
if (has("kafka-node"))
|
|
286
|
+
stack.add("kafka-node");
|
|
287
|
+
if (has("node-rdkafka"))
|
|
288
|
+
stack.add("rdkafka");
|
|
289
|
+
if (has("@aws-sdk/client-kinesis"))
|
|
290
|
+
stack.add("kinesis");
|
|
291
|
+
if (has("pulsar-client"))
|
|
292
|
+
stack.add("pulsar");
|
|
293
|
+
if (has("apache-flink-statefun"))
|
|
294
|
+
stack.add("flink");
|
|
295
|
+
if (has("@google-cloud/dataflow"))
|
|
296
|
+
stack.add("beam");
|
|
297
|
+
if (has("debezium"))
|
|
298
|
+
stack.add("debezium");
|
|
299
|
+
if (has("nats") || has("nats.ws"))
|
|
300
|
+
stack.add("nats");
|
|
301
|
+
if (has("amqplib") || has("amqp-connection-manager"))
|
|
302
|
+
stack.add("rabbitmq");
|
|
303
|
+
// MLOps (Python deps detected via pyproject.toml; here we cover Node bridges + Python signal flag)
|
|
304
|
+
// Most MLOps stacks are Python — detected through pyproject.toml block separately.
|
|
222
305
|
// Databases / ORMs
|
|
223
306
|
if (has("prisma") || has("@prisma/client"))
|
|
224
307
|
stack.add("prisma");
|
|
@@ -358,6 +441,68 @@ export function detect(dir) {
|
|
|
358
441
|
stack.add("ml");
|
|
359
442
|
sig("ml", "python");
|
|
360
443
|
}
|
|
444
|
+
// MLOps lifecycle (training / registry / serving)
|
|
445
|
+
if (ihas("mlflow")) {
|
|
446
|
+
stack.add("mlflow");
|
|
447
|
+
sig("mlops", "mlflow");
|
|
448
|
+
}
|
|
449
|
+
if (ihas("wandb")) {
|
|
450
|
+
stack.add("wandb");
|
|
451
|
+
sig("mlops", "wandb");
|
|
452
|
+
}
|
|
453
|
+
if (ihas("dvc")) {
|
|
454
|
+
stack.add("dvc");
|
|
455
|
+
sig("mlops", "dvc");
|
|
456
|
+
}
|
|
457
|
+
if (ihas("kubeflow") || ihas("kfp")) {
|
|
458
|
+
stack.add("kubeflow");
|
|
459
|
+
sig("mlops", "kubeflow");
|
|
460
|
+
}
|
|
461
|
+
if (ihas("bentoml")) {
|
|
462
|
+
stack.add("bentoml");
|
|
463
|
+
sig("mlops", "bentoml");
|
|
464
|
+
}
|
|
465
|
+
if (ihas("seldon-core")) {
|
|
466
|
+
stack.add("seldon");
|
|
467
|
+
sig("mlops", "seldon");
|
|
468
|
+
}
|
|
469
|
+
if (ihas("kserve")) {
|
|
470
|
+
stack.add("kserve");
|
|
471
|
+
sig("mlops", "kserve");
|
|
472
|
+
}
|
|
473
|
+
if (ihas("sagemaker")) {
|
|
474
|
+
stack.add("sagemaker");
|
|
475
|
+
sig("mlops", "sagemaker");
|
|
476
|
+
}
|
|
477
|
+
if (ihas("google-cloud-aiplatform") || ihas("vertex")) {
|
|
478
|
+
stack.add("vertex-ai");
|
|
479
|
+
sig("mlops", "vertex");
|
|
480
|
+
}
|
|
481
|
+
if (ihas("ray[")) {
|
|
482
|
+
stack.add("ray");
|
|
483
|
+
sig("mlops", "ray");
|
|
484
|
+
}
|
|
485
|
+
// Streaming (Python)
|
|
486
|
+
if (ihas("confluent-kafka") || ihas("kafka-python")) {
|
|
487
|
+
stack.add("kafkajs");
|
|
488
|
+
sig("streaming", "kafka-py");
|
|
489
|
+
}
|
|
490
|
+
if (ihas("pyflink") || ihas("apache-flink")) {
|
|
491
|
+
stack.add("flink");
|
|
492
|
+
sig("streaming", "flink-py");
|
|
493
|
+
}
|
|
494
|
+
if (ihas("apache-beam")) {
|
|
495
|
+
stack.add("beam");
|
|
496
|
+
sig("streaming", "beam-py");
|
|
497
|
+
}
|
|
498
|
+
if (ihas("nats-py")) {
|
|
499
|
+
stack.add("nats");
|
|
500
|
+
sig("streaming", "nats-py");
|
|
501
|
+
}
|
|
502
|
+
if (ihas("debezium")) {
|
|
503
|
+
stack.add("debezium");
|
|
504
|
+
sig("streaming", "debezium");
|
|
505
|
+
}
|
|
361
506
|
if (ihas("opencv") || ihas("ultralytics") || ihas("detectron2")) {
|
|
362
507
|
stack.add("computer-vision");
|
|
363
508
|
sig("ai", "computer-vision");
|
|
@@ -394,7 +539,15 @@ export function detect(dir) {
|
|
|
394
539
|
existsSync(join(dir, "main.py")) ||
|
|
395
540
|
existsSync(join(dir, "app.py")) ||
|
|
396
541
|
existsSync(join(dir, "wsgi.py"));
|
|
397
|
-
|
|
542
|
+
// Python CLI signal: pyproject [project.scripts] OR setup.py with entry_points={'console_scripts'}
|
|
543
|
+
const hasPyCliEntry = pyproject.includes("[project.scripts]") ||
|
|
544
|
+
pyproject.includes("[tool.poetry.scripts]") ||
|
|
545
|
+
(existsSync(join(dir, "setup.py")) && readFileSync(join(dir, "setup.py"), "utf-8").includes("console_scripts"));
|
|
546
|
+
if (hasPyCliEntry) {
|
|
547
|
+
stack.add("python-cli");
|
|
548
|
+
sig("cli", "python-script");
|
|
549
|
+
}
|
|
550
|
+
if (isPyLib && !hasPyApp && !hasPyCliEntry) {
|
|
398
551
|
stack.add("library");
|
|
399
552
|
sig("library", "python");
|
|
400
553
|
}
|
|
@@ -768,6 +921,15 @@ export function detect(dir) {
|
|
|
768
921
|
}
|
|
769
922
|
// ── Project size estimate (Wave 3 lite) ──────────────────
|
|
770
923
|
const projectSize = estimateProjectSize(dir);
|
|
924
|
+
// ── Compliance / regulated-industry docs (Wave 2 supplement) ──
|
|
925
|
+
const complianceFiles = ["ISMS.md", "isms.md", "risk-register.md", "DORA.md", "NIS2.md",
|
|
926
|
+
"bcp.md", "BCP.md", "control-matrix.md", "security-policy.md"];
|
|
927
|
+
if (complianceFiles.some((f) => existsSync(join(dir, f))) ||
|
|
928
|
+
existsSync(join(dir, "compliance")) ||
|
|
929
|
+
existsSync(join(dir, "controls"))) {
|
|
930
|
+
stack.add("compliance-docs");
|
|
931
|
+
sig("regulated", "compliance-docs");
|
|
932
|
+
}
|
|
771
933
|
// ── README mining (Wave 2) ───────────────────────────────
|
|
772
934
|
const readmeKeywords = mineReadmeKeywords(dir);
|
|
773
935
|
for (const kw of readmeKeywords)
|
|
@@ -872,7 +1034,10 @@ function mineReadmeKeywords(dir) {
|
|
|
872
1034
|
"cli": ["command-line", "command line", "cli ", "$ npx ", "subcommand"],
|
|
873
1035
|
"data": ["pipeline", "etl ", "warehouse", "dbt", "airflow", "dataset"],
|
|
874
1036
|
"game": ["game ", "gameplay", "player", "level design"],
|
|
875
|
-
"regulated": ["
|
|
1037
|
+
"regulated": ["dora ict", "nis2", "fedramp", "fisma", "cmmc", "sox compliance",
|
|
1038
|
+
"sarbanes", "regulated entity", "regulated industry",
|
|
1039
|
+
"compliance automation", "audit trail", "iso 27001",
|
|
1040
|
+
"pci compliance", "soc 2 type", "compliance officer"],
|
|
876
1041
|
};
|
|
877
1042
|
for (const [bucket, terms] of Object.entries(buckets)) {
|
|
878
1043
|
if (terms.some((t) => text.includes(t)))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "great-cto",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.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",
|
|
@@ -43,14 +43,26 @@
|
|
|
43
43
|
"startup-tools",
|
|
44
44
|
"solo-founder"
|
|
45
45
|
],
|
|
46
|
-
"homepage": "https://
|
|
46
|
+
"homepage": "https://greatcto.systems",
|
|
47
47
|
"license": "MIT",
|
|
48
|
-
"author":
|
|
48
|
+
"author": {
|
|
49
|
+
"name": "Alexander Velikiy",
|
|
50
|
+
"url": "https://hashnode.com/@Greatcto"
|
|
51
|
+
},
|
|
49
52
|
"repository": {
|
|
50
53
|
"type": "git",
|
|
51
54
|
"url": "git+https://github.com/avelikiy/great_cto.git",
|
|
52
55
|
"directory": "packages/cli"
|
|
53
56
|
},
|
|
57
|
+
"bugs": {
|
|
58
|
+
"url": "https://github.com/avelikiy/great_cto/issues"
|
|
59
|
+
},
|
|
60
|
+
"funding": [
|
|
61
|
+
{
|
|
62
|
+
"type": "github",
|
|
63
|
+
"url": "https://github.com/sponsors/avelikiy"
|
|
64
|
+
}
|
|
65
|
+
],
|
|
54
66
|
"bin": {
|
|
55
67
|
"great-cto": "index.mjs"
|
|
56
68
|
},
|
|
@@ -63,6 +75,7 @@
|
|
|
63
75
|
"scripts": {
|
|
64
76
|
"build": "tsc",
|
|
65
77
|
"test": "npm run build && node --test tests/*.test.mjs",
|
|
78
|
+
"test:e2e": "npm run build && node ../../tests/run-archetype-e2e.mjs",
|
|
66
79
|
"prepublishOnly": "npm run build"
|
|
67
80
|
},
|
|
68
81
|
"devDependencies": {
|