great-cto 1.0.122 → 1.0.124

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.
@@ -169,19 +169,33 @@ const RULES = [
169
169
  },
170
170
  },
171
171
  // ── library (no app framework, just code) ────────
172
+ // Detection priority: explicit "library" or "cli" stack signal from detect.ts (high confidence)
173
+ // Fallback: plain runtime + no web/mobile/infra framework (low confidence)
172
174
  {
173
175
  archetype: "library",
174
176
  score: (d) => {
175
- const hasApp = d.stack.some((t) => ["next.js", "django", "fastapi", "express", "fastify", "nestjs", "react-native", "expo", "terraform"].includes(t));
177
+ // Strong signal: detect.ts found library indicators
178
+ if (d.stack.includes("library") || d.stack.includes("cli")) {
179
+ return 7;
180
+ }
181
+ // Weaker signal: no app framework detected
182
+ const hasApp = d.stack.some((t) => ["next.js", "django", "fastapi", "flask", "express", "fastify", "nestjs", "hono",
183
+ "react-native", "expo", "tauri", "capacitor", "flutter",
184
+ "terraform", "pulumi", "aws-cdk", "helm",
185
+ "embedded", "zephyr", "esp-idf"].includes(t));
176
186
  if (hasApp)
177
187
  return 0;
178
- // Plain Node or Python or Go or Rust with no web/mobile/infra → likely a library
179
188
  if (d.stack.includes("nodejs") || d.stack.includes("python") || d.stack.includes("go") || d.stack.includes("rust")) {
180
189
  return 2;
181
190
  }
182
191
  return 0;
183
192
  },
184
- reason: (_d) => "no web/mobile/infra framework detected — looks like a library/SDK",
193
+ reason: (d) => {
194
+ if (d.stack.includes("library") || d.stack.includes("cli")) {
195
+ return "package.json/pyproject/Cargo.toml indicates a publishable library or CLI";
196
+ }
197
+ return "no web/mobile/infra framework detected — looks like a library/SDK";
198
+ },
185
199
  },
186
200
  ];
187
201
  export function pickArchetype(d) {
package/dist/detect.js CHANGED
@@ -60,6 +60,14 @@ export function detect(dir) {
60
60
  }
61
61
  if (has("expo"))
62
62
  stack.add("expo");
63
+ if (has("@tauri-apps/api") || has("@tauri-apps/cli")) {
64
+ stack.add("tauri");
65
+ sig("desktop", "tauri");
66
+ }
67
+ if (has("@capacitor/core")) {
68
+ stack.add("capacitor");
69
+ sig("mobile", "capacitor");
70
+ }
63
71
  // AI / agents
64
72
  if (has("openai")) {
65
73
  stack.add("openai-sdk");
@@ -73,6 +81,14 @@ export function detect(dir) {
73
81
  stack.add("langchain");
74
82
  sig("ai", "langchain");
75
83
  }
84
+ if (has("@langchain/langgraph")) {
85
+ stack.add("langgraph");
86
+ sig("agent", "langgraph");
87
+ }
88
+ if (has("crewai")) {
89
+ stack.add("crewai");
90
+ sig("agent", "crewai");
91
+ }
76
92
  if (has("llamaindex")) {
77
93
  stack.add("llamaindex");
78
94
  sig("ai", "llamaindex");
@@ -81,6 +97,18 @@ export function detect(dir) {
81
97
  stack.add("mcp");
82
98
  sig("ai", "mcp");
83
99
  }
100
+ if (has("ollama")) {
101
+ stack.add("ollama");
102
+ sig("ai", "ollama");
103
+ }
104
+ if (has("@mastra/core")) {
105
+ stack.add("mastra");
106
+ sig("agent", "mastra");
107
+ }
108
+ if (has("ai")) {
109
+ stack.add("vercel-ai-sdk");
110
+ sig("ai", "vercel-ai-sdk");
111
+ }
84
112
  // Payments / commerce
85
113
  if (has("stripe") || has("@stripe/stripe-js")) {
86
114
  stack.add("stripe");
@@ -94,6 +122,22 @@ export function detect(dir) {
94
122
  stack.add("braintree");
95
123
  sig("commerce", "braintree");
96
124
  }
125
+ if (has("@adyen/api-library")) {
126
+ stack.add("adyen");
127
+ sig("commerce", "adyen");
128
+ }
129
+ if (has("@paddle/paddle-node-sdk")) {
130
+ stack.add("paddle");
131
+ sig("commerce", "paddle");
132
+ }
133
+ if (has("@lemonsqueezy/lemonsqueezy.js")) {
134
+ stack.add("lemonsqueezy");
135
+ sig("commerce", "lemonsqueezy");
136
+ }
137
+ if (has("polar-sh")) {
138
+ stack.add("polar");
139
+ sig("commerce", "polar");
140
+ }
97
141
  // Auth
98
142
  if (has("next-auth") || has("@auth/core"))
99
143
  stack.add("auth");
@@ -101,25 +145,66 @@ export function detect(dir) {
101
145
  stack.add("clerk");
102
146
  if (has("@supabase/supabase-js"))
103
147
  stack.add("supabase");
148
+ if (has("lucia")) {
149
+ stack.add("lucia");
150
+ }
151
+ if (has("@workos-inc/node")) {
152
+ stack.add("workos");
153
+ }
104
154
  // Databases / ORMs
105
155
  if (has("prisma") || has("@prisma/client"))
106
156
  stack.add("prisma");
107
157
  if (has("drizzle-orm"))
108
158
  stack.add("drizzle");
159
+ if (has("kysely"))
160
+ stack.add("kysely");
109
161
  if (has("typeorm"))
110
162
  stack.add("typeorm");
111
163
  if (has("mongodb") || has("mongoose"))
112
164
  stack.add("mongodb");
113
165
  if (has("pg") || has("postgres"))
114
166
  stack.add("postgres");
167
+ if (has("@neondatabase/serverless")) {
168
+ stack.add("neon");
169
+ sig("edge", "neon");
170
+ }
171
+ if (has("@planetscale/database")) {
172
+ stack.add("planetscale");
173
+ sig("edge", "planetscale");
174
+ }
175
+ if (has("@libsql/client")) {
176
+ stack.add("turso");
177
+ sig("edge", "turso");
178
+ }
115
179
  if (has("mysql") || has("mysql2"))
116
180
  stack.add("mysql");
117
181
  if (has("redis") || has("ioredis"))
118
182
  stack.add("redis");
183
+ if (has("duckdb") || has("@duckdb/node-api")) {
184
+ stack.add("duckdb");
185
+ sig("data", "duckdb");
186
+ }
119
187
  // Testing
120
188
  if (has("jest") || has("vitest") || has("mocha") || has("@playwright/test") || has("playwright")) {
121
189
  sig("tests", "package.json");
122
190
  }
191
+ // Library detection (npm package intended for distribution)
192
+ // Signals: has "main" or "exports", NOT "private:true", NOT a typical app structure
193
+ const isPublishable = !pkg.private && (pkg.main || pkg.exports || pkg.module || pkg.type === "module");
194
+ const hasAppStructure = existsSync(join(dir, "pages")) ||
195
+ existsSync(join(dir, "app")) ||
196
+ existsSync(join(dir, "src/pages")) ||
197
+ existsSync(join(dir, "src/app")) ||
198
+ existsSync(join(dir, "public/index.html"));
199
+ const hasBin = !!pkg.bin;
200
+ if (isPublishable && !hasAppStructure) {
201
+ stack.add("library");
202
+ sig("library", "package.json");
203
+ }
204
+ if (hasBin) {
205
+ stack.add("cli");
206
+ sig("library", "bin");
207
+ }
123
208
  }
124
209
  catch { /* ignore malformed */ }
125
210
  }
@@ -157,20 +242,96 @@ export function detect(dir) {
157
242
  stack.add("langchain");
158
243
  sig("ai", "langchain");
159
244
  }
245
+ if (ihas("langgraph")) {
246
+ stack.add("langgraph");
247
+ sig("agent", "langgraph");
248
+ }
249
+ if (ihas("crewai")) {
250
+ stack.add("crewai");
251
+ sig("agent", "crewai");
252
+ }
253
+ if (ihas("autogen-agentchat") || ihas("pyautogen")) {
254
+ stack.add("autogen");
255
+ sig("agent", "autogen");
256
+ }
257
+ if (ihas("dspy-ai")) {
258
+ stack.add("dspy");
259
+ sig("ai", "dspy");
260
+ }
261
+ if (ihas("vllm")) {
262
+ stack.add("vllm");
263
+ sig("ai", "vllm");
264
+ }
265
+ if (ihas("ollama")) {
266
+ stack.add("ollama");
267
+ sig("ai", "ollama");
268
+ }
269
+ if (ihas("ragas") || ihas("deepeval")) {
270
+ stack.add("llm-eval");
271
+ sig("ai", "llm-eval");
272
+ }
160
273
  if (ihas("llama-index") || ihas("llamaindex"))
161
274
  stack.add("llamaindex");
162
275
  if (ihas("torch") || ihas("tensorflow") || ihas("scikit-learn")) {
163
276
  stack.add("ml");
164
277
  sig("ml", "python");
165
278
  }
166
- if (ihas("pandas") || ihas("dask") || ihas("airflow") || ihas("prefect")) {
279
+ if (ihas("opencv") || ihas("ultralytics") || ihas("detectron2")) {
280
+ stack.add("computer-vision");
281
+ sig("ai", "computer-vision");
282
+ }
283
+ if (ihas("pandas") || ihas("dask") || ihas("airflow") || ihas("prefect") || ihas("dagster")) {
167
284
  stack.add("data-pipeline");
168
285
  sig("data", "python");
169
286
  }
287
+ if (ihas("polars")) {
288
+ stack.add("polars");
289
+ sig("data", "polars");
290
+ }
291
+ if (ihas("duckdb")) {
292
+ stack.add("duckdb");
293
+ sig("data", "duckdb");
294
+ }
295
+ if (ihas("pyiceberg")) {
296
+ stack.add("iceberg");
297
+ sig("data", "iceberg");
298
+ }
299
+ if (ihas("dbt-core") || ihas("dbt-")) {
300
+ stack.add("dbt");
301
+ sig("data", "dbt");
302
+ }
170
303
  if (ihas("stripe")) {
171
304
  stack.add("stripe");
172
305
  sig("commerce", "stripe");
173
306
  }
307
+ // Library detection — Python package intended for distribution
308
+ // Signal: has setup.py / pyproject.toml [project] or [tool.poetry] without 'private = true'
309
+ const isPyLib = pyproject.includes("[project]") || pyproject.includes("[tool.poetry]") ||
310
+ existsSync(join(dir, "setup.py"));
311
+ const hasPyApp = existsSync(join(dir, "manage.py")) ||
312
+ existsSync(join(dir, "main.py")) ||
313
+ existsSync(join(dir, "app.py")) ||
314
+ existsSync(join(dir, "wsgi.py"));
315
+ if (isPyLib && !hasPyApp) {
316
+ stack.add("library");
317
+ sig("library", "python");
318
+ }
319
+ }
320
+ catch { /* ignore */ }
321
+ }
322
+ // ── Flutter / Dart ────────────────────────────────────────
323
+ if (existsSync(join(dir, "pubspec.yaml"))) {
324
+ sig("flutter", "pubspec.yaml");
325
+ stack.add("flutter");
326
+ languages.add("dart");
327
+ try {
328
+ const pubspec = readFileSync(join(dir, "pubspec.yaml"), "utf-8").toLowerCase();
329
+ if (pubspec.includes("flutter:"))
330
+ sig("mobile", "flutter");
331
+ if (pubspec.includes("flutter_bloc"))
332
+ stack.add("bloc");
333
+ if (pubspec.includes("riverpod"))
334
+ stack.add("riverpod");
174
335
  }
175
336
  catch { /* ignore */ }
176
337
  }
@@ -185,6 +346,20 @@ export function detect(dir) {
185
346
  stack.add("stripe");
186
347
  if (gomod.includes("openai-go"))
187
348
  stack.add("openai-sdk");
349
+ if (gomod.includes("anthropic-sdk-go"))
350
+ stack.add("anthropic-sdk");
351
+ if (gomod.includes("gin-gonic/gin"))
352
+ stack.add("gin");
353
+ if (gomod.includes("labstack/echo"))
354
+ stack.add("echo");
355
+ if (gomod.includes("go-chi/chi"))
356
+ stack.add("chi");
357
+ // Library detection: no main package + intended as module
358
+ const hasMainGo = existsSync(join(dir, "main.go")) || existsSync(join(dir, "cmd"));
359
+ if (!hasMainGo) {
360
+ stack.add("library");
361
+ sig("library", "go");
362
+ }
188
363
  }
189
364
  catch { /* ignore */ }
190
365
  }
@@ -198,6 +373,17 @@ export function detect(dir) {
198
373
  if (cargo.includes("actix-web") || cargo.includes("axum") || cargo.includes("rocket")) {
199
374
  sig("web-rust", "Cargo.toml");
200
375
  }
376
+ if (cargo.includes("embassy")) {
377
+ stack.add("embassy");
378
+ sig("embedded", "embassy");
379
+ }
380
+ // Library detection: [lib] section without [[bin]]
381
+ const hasLib = cargo.includes("[lib]") || (!cargo.includes("[[bin]]") && cargo.includes("[package]"));
382
+ const hasBin = cargo.includes("[[bin]]") || existsSync(join(dir, "src/main.rs"));
383
+ if (hasLib && !hasBin) {
384
+ stack.add("library");
385
+ sig("library", "rust");
386
+ }
201
387
  }
202
388
  catch { /* ignore */ }
203
389
  }
@@ -230,18 +416,44 @@ export function detect(dir) {
230
416
  sig("infra", "terraform");
231
417
  stack.add("terraform");
232
418
  }
419
+ if (existsSync(join(dir, "Pulumi.yaml")) || existsSync(join(dir, "Pulumi.yml"))) {
420
+ sig("infra", "pulumi");
421
+ stack.add("pulumi");
422
+ }
423
+ if (existsSync(join(dir, "cdk.json"))) {
424
+ sig("infra", "aws-cdk");
425
+ stack.add("aws-cdk");
426
+ }
233
427
  if (existsSync(join(dir, "Chart.yaml")) || existsSync(join(dir, "values.yaml"))) {
234
428
  sig("infra", "helm");
235
429
  stack.add("helm");
236
430
  }
431
+ if (existsSync(join(dir, "helmfile.yaml")) || existsSync(join(dir, "helmfile.yml"))) {
432
+ sig("infra", "helmfile");
433
+ stack.add("helmfile");
434
+ }
237
435
  if (safeGlob(dir, /kustomization\.ya?ml$/)) {
238
436
  sig("infra", "kustomize");
239
437
  stack.add("kubernetes");
240
438
  }
439
+ if (existsSync(join(dir, "argocd")) || safeGlob(dir, /argocd-application\.ya?ml$/)) {
440
+ sig("infra", "argocd");
441
+ stack.add("argocd");
442
+ }
241
443
  if (existsSync(join(dir, "Dockerfile")) || existsSync(join(dir, "docker-compose.yml"))) {
242
444
  sig("docker", "Dockerfile");
243
445
  stack.add("docker");
244
446
  }
447
+ if (existsSync(join(dir, "dbt_project.yml"))) {
448
+ sig("data", "dbt");
449
+ stack.add("dbt");
450
+ stack.add("data-pipeline");
451
+ }
452
+ if (existsSync(join(dir, "dagster.yaml")) || existsSync(join(dir, "workspace.yaml"))) {
453
+ sig("data", "dagster");
454
+ stack.add("dagster");
455
+ stack.add("data-pipeline");
456
+ }
245
457
  // ── Smart contracts ──────────────────────────────────────
246
458
  if (existsSync(join(dir, "hardhat.config.js")) ||
247
459
  existsSync(join(dir, "hardhat.config.ts")) ||
@@ -262,6 +474,16 @@ export function detect(dir) {
262
474
  sig("embedded", "platformio/sdk");
263
475
  stack.add("embedded");
264
476
  }
477
+ if (existsSync(join(dir, "west.yml")) || existsSync(join(dir, "zephyr"))) {
478
+ sig("embedded", "zephyr");
479
+ stack.add("embedded");
480
+ stack.add("zephyr");
481
+ }
482
+ if (existsSync(join(dir, "CMakePresets.json")) && existsSync(join(dir, "components"))) {
483
+ sig("embedded", "esp-idf");
484
+ stack.add("embedded");
485
+ stack.add("esp-idf");
486
+ }
265
487
  // ── Package manager ──────────────────────────────────────
266
488
  let packageManager = null;
267
489
  if (existsSync(join(dir, "pnpm-lock.yaml")))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "great-cto",
3
- "version": "1.0.122",
3
+ "version": "1.0.124",
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",