@wneng/create-keel 0.2.1 → 0.3.2

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.
Files changed (29) hide show
  1. package/README.md +26 -1
  2. package/dist/index.js +612 -40
  3. package/dist/index.js.map +1 -1
  4. package/package.json +2 -1
  5. package/src/standards/templates/coding-style-dart.md.eta +49 -0
  6. package/src/standards/templates/coding-style-go.md.eta +52 -0
  7. package/src/standards/templates/coding-style-java.md.eta +50 -0
  8. package/src/standards/templates/coding-style-python.md.eta +51 -0
  9. package/src/standards/templates/coding-style-rust.md.eta +52 -0
  10. package/src/standards/templates/coding-style-typescript.md.eta +50 -0
  11. package/src/standards/templates/dependency-cruiser.config.cjs.eta +57 -0
  12. package/src/standards/templates/design-tokens.ts.eta +37 -0
  13. package/src/standards/templates/module-boundaries.md.eta +82 -0
  14. package/src/standards/templates/tech-stack-agent.md.eta +35 -0
  15. package/src/standards/templates/tech-stack-miniapp.md.eta +34 -0
  16. package/src/standards/templates/tech-stack-mobile.md.eta +36 -0
  17. package/src/standards/templates/tech-stack-server.md.eta +50 -0
  18. package/src/standards/templates/tech-stack-web.md.eta +36 -0
  19. package/src/standards/templates/ui-design-system.md.eta +70 -0
  20. package/src/templates/ci-gitee/files/PULL_REQUEST_TEMPLATE.md +62 -0
  21. package/src/templates/ci-gitee/fragment.yaml +4 -1
  22. package/src/templates/ci-github/files/PULL_REQUEST_TEMPLATE.md +62 -0
  23. package/src/templates/ci-github/fragment.yaml +4 -1
  24. package/src/templates/docs-skeleton/files/README.md +3 -3
  25. package/src/templates/docs-skeleton/files/governance-checklists.md +3 -3
  26. package/src/templates/docs-skeleton/files/governance-security.md +6 -2
  27. package/src/templates/docs-skeleton/files/usage-quickstart.md +11 -0
  28. package/src/templates/root-files/files/CODEOWNERS +40 -0
  29. package/src/templates/root-files/fragment.yaml +3 -0
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/version.ts
4
- var SCAFFOLDER_VERSION = "0.2.1";
4
+ var SCAFFOLDER_VERSION = "0.3.2";
5
5
 
6
6
  // src/schema/options.ts
7
7
  import Ajv from "ajv";
@@ -34,7 +34,8 @@ var OPTIONS_FIELD_ORDER = [
34
34
  "gitLfs",
35
35
  "integrations",
36
36
  "ci",
37
- "roles"
37
+ "roles",
38
+ "multiApp"
38
39
  ];
39
40
  var OPTIONS_SCHEMA = {
40
41
  $schema: "http://json-schema.org/draft-07/schema#",
@@ -56,7 +57,8 @@ var OPTIONS_SCHEMA = {
56
57
  "gitLfs",
57
58
  "integrations",
58
59
  "ci",
59
- "roles"
60
+ "roles",
61
+ "multiApp"
60
62
  ],
61
63
  properties: {
62
64
  projectName: {
@@ -79,7 +81,8 @@ var OPTIONS_SCHEMA = {
79
81
  type: "array",
80
82
  items: { type: "string", enum: [...ROLE_KINDS] },
81
83
  uniqueItems: true
82
- }
84
+ },
85
+ multiApp: { type: "boolean" }
83
86
  }
84
87
  };
85
88
  var ajv = new Ajv({ allErrors: true, strict: true });
@@ -334,6 +337,27 @@ function parseLayerArg(raw) {
334
337
  return out;
335
338
  }
336
339
 
340
+ // src/standards/tier.ts
341
+ var ADOPTION_TIERS = ["lite", "standard", "full"];
342
+ function isAdoptionTier(value) {
343
+ return ADOPTION_TIERS.includes(value);
344
+ }
345
+ function parseTier(input) {
346
+ if (typeof input !== "string" || input.length === 0) {
347
+ throw new UserInputError(
348
+ `invalid --engineering-standards value: ${JSON.stringify(input)}`,
349
+ `\u53EF\u9009\u503C\uFF1A${ADOPTION_TIERS.join(" | ")}`
350
+ );
351
+ }
352
+ if (!isAdoptionTier(input)) {
353
+ throw new UserInputError(
354
+ `invalid --engineering-standards value: ${JSON.stringify(input)}`,
355
+ `\u53EF\u9009\u503C\uFF1A${ADOPTION_TIERS.join(" | ")}`
356
+ );
357
+ }
358
+ return input;
359
+ }
360
+
337
361
  // src/cli.ts
338
362
  async function run(io) {
339
363
  const args = io.argv.slice(2);
@@ -387,6 +411,35 @@ function parseCreateArgs(args) {
387
411
  let ci;
388
412
  let roles;
389
413
  let sampleFeature = false;
414
+ let engineeringStandards;
415
+ let engineeringStandardsExplicit = false;
416
+ let multiApp = false;
417
+ let backend;
418
+ let frontend;
419
+ let mobile;
420
+ let miniapp;
421
+ let agent;
422
+ let deploy;
423
+ let contract;
424
+ let ai;
425
+ let license;
426
+ let gitLfs;
427
+ let integrations;
428
+ function takeEnum(flag, next, enumValues) {
429
+ if (next === void 0 || next.startsWith("--")) {
430
+ throw new UserInputError(
431
+ `${flag} requires a value`,
432
+ `\u53EF\u9009\u503C\uFF1A${enumValues.join(" | ")}`
433
+ );
434
+ }
435
+ if (!enumValues.includes(next)) {
436
+ throw new UserInputError(
437
+ `invalid ${flag} value: ${JSON.stringify(next)}`,
438
+ `\u53EF\u9009\u503C\uFF1A${enumValues.join(" | ")}`
439
+ );
440
+ }
441
+ return next;
442
+ }
390
443
  for (let i = 0; i < args.length; i += 1) {
391
444
  const a = args[i];
392
445
  switch (a) {
@@ -408,6 +461,74 @@ function parseCreateArgs(args) {
408
461
  case "--sample-feature":
409
462
  sampleFeature = true;
410
463
  break;
464
+ case "--multi-app":
465
+ multiApp = true;
466
+ break;
467
+ case "--gitLfs":
468
+ case "--git-lfs":
469
+ gitLfs = true;
470
+ break;
471
+ case "--integrations":
472
+ integrations = true;
473
+ break;
474
+ case "--backend": {
475
+ backend = takeEnum("--backend", args[i + 1], BACKEND_LANGUAGES);
476
+ i += 1;
477
+ break;
478
+ }
479
+ case "--frontend": {
480
+ frontend = takeEnum("--frontend", args[i + 1], FRONTEND_FRAMEWORKS);
481
+ i += 1;
482
+ break;
483
+ }
484
+ case "--mobile": {
485
+ mobile = takeEnum("--mobile", args[i + 1], MOBILE_KINDS);
486
+ i += 1;
487
+ break;
488
+ }
489
+ case "--miniapp": {
490
+ miniapp = takeEnum("--miniapp", args[i + 1], MINIAPP_KINDS);
491
+ i += 1;
492
+ break;
493
+ }
494
+ case "--agent": {
495
+ agent = takeEnum("--agent", args[i + 1], AGENT_KINDS);
496
+ i += 1;
497
+ break;
498
+ }
499
+ case "--deploy": {
500
+ deploy = takeEnum("--deploy", args[i + 1], DEPLOY_TARGETS);
501
+ i += 1;
502
+ break;
503
+ }
504
+ case "--contract": {
505
+ contract = takeEnum("--contract", args[i + 1], CONTRACT_KINDS);
506
+ i += 1;
507
+ break;
508
+ }
509
+ case "--ai": {
510
+ ai = takeEnum("--ai", args[i + 1], AI_TOOLS);
511
+ i += 1;
512
+ break;
513
+ }
514
+ case "--license": {
515
+ license = takeEnum("--license", args[i + 1], LICENSE_KINDS);
516
+ i += 1;
517
+ break;
518
+ }
519
+ case "--engineering-standards": {
520
+ const next = args[i + 1];
521
+ if (next === void 0 || next.startsWith("--")) {
522
+ throw new UserInputError(
523
+ "--engineering-standards requires a tier argument",
524
+ `\u53EF\u9009\u503C\uFF1A${ADOPTION_TIERS.join(" | ")}`
525
+ );
526
+ }
527
+ engineeringStandards = parseTier(next);
528
+ engineeringStandardsExplicit = true;
529
+ i += 1;
530
+ break;
531
+ }
411
532
  case "--config": {
412
533
  const next = args[i + 1];
413
534
  if (next === void 0 || next.startsWith("--")) {
@@ -500,7 +621,20 @@ function parseCreateArgs(args) {
500
621
  ...config !== void 0 ? { config } : {},
501
622
  ...ci !== void 0 ? { ci } : {},
502
623
  ...roles !== void 0 ? { roles } : {},
503
- ...sampleFeature ? { sampleFeature: true } : {}
624
+ ...sampleFeature ? { sampleFeature: true } : {},
625
+ ...engineeringStandards !== void 0 ? { engineeringStandards } : {},
626
+ ...multiApp ? { multiApp: true } : {},
627
+ ...backend !== void 0 ? { backend } : {},
628
+ ...frontend !== void 0 ? { frontend } : {},
629
+ ...mobile !== void 0 ? { mobile } : {},
630
+ ...miniapp !== void 0 ? { miniapp } : {},
631
+ ...agent !== void 0 ? { agent } : {},
632
+ ...deploy !== void 0 ? { deploy } : {},
633
+ ...contract !== void 0 ? { contract } : {},
634
+ ...ai !== void 0 ? { ai } : {},
635
+ ...license !== void 0 ? { license } : {},
636
+ ...gitLfs !== void 0 ? { gitLfs } : {},
637
+ ...integrations !== void 0 ? { integrations } : {}
504
638
  };
505
639
  if (sampleFeature && dryRun) {
506
640
  throw new UserInputError(
@@ -508,6 +642,12 @@ function parseCreateArgs(args) {
508
642
  "\u8BF7\u53BB\u6389\u4E00\u9879\u540E\u91CD\u8BD5"
509
643
  );
510
644
  }
645
+ if (!engineeringStandardsExplicit) {
646
+ return {
647
+ projectName,
648
+ flags: { ...flags, engineeringStandards: "standard" }
649
+ };
650
+ }
511
651
  return { projectName, flags };
512
652
  }
513
653
  async function dispatchFeature(io, args) {
@@ -569,6 +709,21 @@ function helpText() {
569
709
  " --roles <list> Comma-separated role directories to scaffold",
570
710
  " (" + ROLE_KINDS.join("|") + "; default: none)",
571
711
  " --sample-feature After create, scaffold the user-signup sample feature",
712
+ " --engineering-standards <tier>",
713
+ " Adoption tier for engineering standards: " + ADOPTION_TIERS.join("|"),
714
+ " (default in --yes mode: standard; omitted in interactive mode)",
715
+ " --multi-app Initialise the project in multi-app mode (<env>/apps/<name>/)",
716
+ " --backend <kind> " + BACKEND_LANGUAGES.join("|"),
717
+ " --frontend <kind> " + FRONTEND_FRAMEWORKS.join("|"),
718
+ " --mobile <kind> " + MOBILE_KINDS.join("|"),
719
+ " --miniapp <kind> " + MINIAPP_KINDS.join("|"),
720
+ " --agent <kind> " + AGENT_KINDS.join("|"),
721
+ " --deploy <kind> " + DEPLOY_TARGETS.join("|"),
722
+ " --contract <kind> " + CONTRACT_KINDS.join("|"),
723
+ " --ai <kind> " + AI_TOOLS.join("|"),
724
+ " --license <kind> " + LICENSE_KINDS.join("|"),
725
+ " --gitLfs Enable Git LFS in the generated project",
726
+ " --integrations Enable docs/06-\u96C6\u6210\u5BF9\u63A5/ at create time",
572
727
  " -h, --help Show this help",
573
728
  " -v, --version Show the scaffolder version",
574
729
  "",
@@ -603,7 +758,7 @@ function featureHelpText() {
603
758
  }
604
759
 
605
760
  // src/orchestrator.ts
606
- import * as path6 from "node:path";
761
+ import * as path7 from "node:path";
607
762
 
608
763
  // src/input/defaults.ts
609
764
  var OPTION_DEFAULTS = {
@@ -619,7 +774,8 @@ var OPTION_DEFAULTS = {
619
774
  gitLfs: false,
620
775
  integrations: false,
621
776
  ci: "gitee",
622
- roles: []
777
+ roles: [],
778
+ multiApp: false
623
779
  };
624
780
  function buildDefaultOptions(projectName) {
625
781
  return { projectName, ...OPTION_DEFAULTS };
@@ -654,7 +810,8 @@ var PARTIAL_OPTIONS_SCHEMA = {
654
810
  type: "array",
655
811
  items: { type: "string", enum: [...ROLE_KINDS] },
656
812
  uniqueItems: true
657
- }
813
+ },
814
+ multiApp: { type: "boolean" }
658
815
  }
659
816
  };
660
817
  var ajv2 = new Ajv2({ allErrors: true, strict: true });
@@ -664,8 +821,8 @@ var validate = ajv2.compile(
664
821
  function formatIssues() {
665
822
  const errs = validate.errors ?? [];
666
823
  return errs.map((e) => {
667
- const path10 = e.instancePath || (e.params && "missingProperty" in e.params ? `/${String(e.params.missingProperty)}` : "<root>");
668
- return `${path10}: ${e.message ?? "validation error"}`;
824
+ const path11 = e.instancePath || (e.params && "missingProperty" in e.params ? `/${String(e.params.missingProperty)}` : "<root>");
825
+ return `${path11}: ${e.message ?? "validation error"}`;
669
826
  }).join("; ");
670
827
  }
671
828
  async function loadConfigFile(filePath) {
@@ -901,7 +1058,8 @@ async function promptForOptions(input) {
901
1058
  gitLfs,
902
1059
  integrations,
903
1060
  ci,
904
- roles
1061
+ roles,
1062
+ multiApp: OPTION_DEFAULTS.multiApp
905
1063
  };
906
1064
  }
907
1065
  async function chooseAxis(prompter, prefilled, question, domain, def, report, axisName) {
@@ -1163,7 +1321,8 @@ function buildRenderContext(input) {
1163
1321
  year: String(now.getUTCFullYear()),
1164
1322
  generatedAt: now.toISOString(),
1165
1323
  scaffolderVersion: input.scaffolderVersion,
1166
- ...input.feature !== void 0 ? { feature: input.feature } : {}
1324
+ ...input.feature !== void 0 ? { feature: input.feature } : {},
1325
+ ...input.standards !== void 0 ? { standards: input.standards } : {}
1167
1326
  };
1168
1327
  }
1169
1328
  function buildFeatureContext(slug, nextAdrNumber) {
@@ -1204,7 +1363,10 @@ function toStrictContext(ctx) {
1204
1363
  // `feature` is intentionally only attached when the orchestrator
1205
1364
  // populated it; non-feature templates that try to read it will hit
1206
1365
  // the proxy's "undefined template variable" branch.
1207
- ...ctx.feature !== void 0 ? { feature: ctx.feature } : {}
1366
+ ...ctx.feature !== void 0 ? { feature: ctx.feature } : {},
1367
+ // `standards` follows the same pattern; only the engineering-standards
1368
+ // plan populates it.
1369
+ ...ctx.standards !== void 0 ? { standards: ctx.standards } : {}
1208
1370
  };
1209
1371
  return new Proxy(base, {
1210
1372
  get(target, prop, receiver) {
@@ -1386,6 +1548,10 @@ var METADATA_SCHEMA = {
1386
1548
  type: "array",
1387
1549
  items: { type: "string", pattern: FEATURE_SLUG_PATTERN2 },
1388
1550
  uniqueItems: true
1551
+ },
1552
+ engineeringStandards: {
1553
+ type: "string",
1554
+ enum: [...ADOPTION_TIERS]
1389
1555
  }
1390
1556
  }
1391
1557
  };
@@ -1422,7 +1588,8 @@ var METADATA_TOP_ORDER = [
1422
1588
  "generatedAt",
1423
1589
  "options",
1424
1590
  "templateFragments",
1425
- "features"
1591
+ "features",
1592
+ "engineeringStandards"
1426
1593
  ];
1427
1594
  function canonicalOptions(options) {
1428
1595
  const out = {};
@@ -1446,6 +1613,11 @@ function canonicalMetadata(metadata) {
1446
1613
  if (features !== void 0 && features.length > 0) {
1447
1614
  out[key] = [...features];
1448
1615
  }
1616
+ } else if (key === "engineeringStandards") {
1617
+ const tier = metadata.engineeringStandards;
1618
+ if (tier !== void 0) {
1619
+ out[key] = tier;
1620
+ }
1449
1621
  } else {
1450
1622
  out[key] = metadata[key];
1451
1623
  }
@@ -1483,7 +1655,8 @@ function buildMetadata(params) {
1483
1655
  generatedAt: now.toISOString(),
1484
1656
  options: params.options,
1485
1657
  templateFragments: [...params.templateFragments],
1486
- ...params.features !== void 0 ? { features: [...params.features] } : {}
1658
+ ...params.features !== void 0 ? { features: [...params.features] } : {},
1659
+ ...params.engineeringStandards !== void 0 ? { engineeringStandards: params.engineeringStandards } : {}
1487
1660
  };
1488
1661
  }
1489
1662
 
@@ -1493,6 +1666,7 @@ function appendScaffolderMetadata(input) {
1493
1666
  scaffolderVersion: input.scaffolderVersion,
1494
1667
  options: input.options,
1495
1668
  templateFragments: input.templateFragments,
1669
+ ...input.engineeringStandards !== void 0 ? { engineeringStandards: input.engineeringStandards } : {},
1496
1670
  ...input.now !== void 0 ? { now: input.now } : {}
1497
1671
  });
1498
1672
  const file = {
@@ -1695,6 +1869,375 @@ function verbosityFromFlags(flags) {
1695
1869
  return "default";
1696
1870
  }
1697
1871
 
1872
+ // src/standards/plan.ts
1873
+ import { accessSync as accessSync2 } from "node:fs";
1874
+ import * as path6 from "node:path";
1875
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
1876
+
1877
+ // src/standards/stack-detector.ts
1878
+ function backendManifest(opt) {
1879
+ switch (opt) {
1880
+ case "node":
1881
+ return { env: "server", language: "typescript", manifestPath: "server/package.json", packageManager: "npm" };
1882
+ case "java":
1883
+ return { env: "server", language: "java", manifestPath: "server/pom.xml", packageManager: "maven" };
1884
+ case "go":
1885
+ return { env: "server", language: "go", manifestPath: "server/go.mod", packageManager: "gomod" };
1886
+ case "python":
1887
+ return { env: "server", language: "python", manifestPath: "server/pyproject.toml", packageManager: "pip" };
1888
+ case "none":
1889
+ return null;
1890
+ }
1891
+ }
1892
+ function frontendManifest(opt) {
1893
+ switch (opt) {
1894
+ case "react":
1895
+ case "vue":
1896
+ return { env: "web", language: "typescript", manifestPath: "web/package.json", packageManager: "npm" };
1897
+ case "none":
1898
+ return null;
1899
+ }
1900
+ }
1901
+ function mobileManifest(opt) {
1902
+ switch (opt) {
1903
+ case "flutter":
1904
+ return { env: "mobile", language: "dart", manifestPath: "mobile/pubspec.yaml", packageManager: "pub" };
1905
+ case "react-native":
1906
+ return { env: "mobile", language: "typescript", manifestPath: "mobile/package.json", packageManager: "npm" };
1907
+ case "none":
1908
+ return null;
1909
+ }
1910
+ }
1911
+ function miniappManifest(opt) {
1912
+ switch (opt) {
1913
+ case "wechat":
1914
+ return { env: "miniapp", language: "typescript", manifestPath: "miniapp/package.json", packageManager: "npm" };
1915
+ case "none":
1916
+ return null;
1917
+ }
1918
+ }
1919
+ function agentManifest(opt) {
1920
+ switch (opt) {
1921
+ case "rust-desktop":
1922
+ return { env: "agent", language: "rust", manifestPath: "agent/Cargo.toml", packageManager: "cargo" };
1923
+ case "none":
1924
+ return null;
1925
+ }
1926
+ }
1927
+ var SEED_DEPS_NODE_SERVER = [
1928
+ { name: "typescript", version: "5.5.4", policy: "minor" },
1929
+ { name: "@types/node", version: "20.14.10", policy: "minor" }
1930
+ ];
1931
+ var SEED_DEPS_JAVA_SERVER = [
1932
+ { name: "spring-boot-starter-web", version: "3.3.2", policy: "minor" },
1933
+ { name: "spring-boot-starter-validation", version: "3.3.2", policy: "minor" }
1934
+ ];
1935
+ var SEED_DEPS_GO_SERVER = [
1936
+ { name: "github.com/gin-gonic/gin", version: "1.10.0", policy: "minor" }
1937
+ ];
1938
+ var SEED_DEPS_PYTHON_SERVER = [
1939
+ { name: "fastapi", version: "0.111.1", policy: "minor" },
1940
+ { name: "pydantic", version: "2.8.2", policy: "minor" }
1941
+ ];
1942
+ var SEED_DEPS_REACT_WEB = [
1943
+ { name: "react", version: "18.3.1", policy: "minor" },
1944
+ { name: "react-dom", version: "18.3.1", policy: "minor" },
1945
+ { name: "typescript", version: "5.5.4", policy: "minor" }
1946
+ ];
1947
+ var SEED_DEPS_VUE_WEB = [
1948
+ { name: "vue", version: "3.4.34", policy: "minor" },
1949
+ { name: "typescript", version: "5.5.4", policy: "minor" }
1950
+ ];
1951
+ var SEED_DEPS_FLUTTER_MOBILE = [
1952
+ { name: "flutter", version: "3.22.0", policy: "minor" }
1953
+ ];
1954
+ var SEED_DEPS_REACT_NATIVE_MOBILE = [
1955
+ { name: "react-native", version: "0.74.3", policy: "minor" },
1956
+ { name: "react", version: "18.3.1", policy: "minor" }
1957
+ ];
1958
+ var SEED_DEPS_WECHAT_MINIAPP = [
1959
+ { name: "typescript", version: "5.5.4", policy: "minor" }
1960
+ ];
1961
+ var SEED_DEPS_RUST_AGENT = [
1962
+ { name: "tokio", version: "1.39.2", policy: "minor" },
1963
+ { name: "serde", version: "1.0.204", policy: "minor" }
1964
+ ];
1965
+ function seedDeps(options, env) {
1966
+ switch (env) {
1967
+ case "server":
1968
+ switch (options.backend) {
1969
+ case "node":
1970
+ return SEED_DEPS_NODE_SERVER;
1971
+ case "java":
1972
+ return SEED_DEPS_JAVA_SERVER;
1973
+ case "go":
1974
+ return SEED_DEPS_GO_SERVER;
1975
+ case "python":
1976
+ return SEED_DEPS_PYTHON_SERVER;
1977
+ case "none":
1978
+ return [];
1979
+ }
1980
+ break;
1981
+ case "web":
1982
+ switch (options.frontend) {
1983
+ case "react":
1984
+ return SEED_DEPS_REACT_WEB;
1985
+ case "vue":
1986
+ return SEED_DEPS_VUE_WEB;
1987
+ case "none":
1988
+ return [];
1989
+ }
1990
+ break;
1991
+ case "mobile":
1992
+ switch (options.mobile) {
1993
+ case "flutter":
1994
+ return SEED_DEPS_FLUTTER_MOBILE;
1995
+ case "react-native":
1996
+ return SEED_DEPS_REACT_NATIVE_MOBILE;
1997
+ case "none":
1998
+ return [];
1999
+ }
2000
+ break;
2001
+ case "miniapp":
2002
+ switch (options.miniapp) {
2003
+ case "wechat":
2004
+ return SEED_DEPS_WECHAT_MINIAPP;
2005
+ case "none":
2006
+ return [];
2007
+ }
2008
+ break;
2009
+ case "agent":
2010
+ switch (options.agent) {
2011
+ case "rust-desktop":
2012
+ return SEED_DEPS_RUST_AGENT;
2013
+ case "none":
2014
+ return [];
2015
+ }
2016
+ break;
2017
+ }
2018
+ return [];
2019
+ }
2020
+ function detectRuntimeManifests(options) {
2021
+ const detectors = [
2022
+ backendManifest(options.backend),
2023
+ frontendManifest(options.frontend),
2024
+ mobileManifest(options.mobile),
2025
+ miniappManifest(options.miniapp),
2026
+ agentManifest(options.agent)
2027
+ ];
2028
+ return detectors.filter((m) => m !== null).map((m) => ({ ...m, dependencies: seedDeps(options, m.env) }));
2029
+ }
2030
+
2031
+ // src/standards/plan.ts
2032
+ async function buildStandardsPlan(input) {
2033
+ const templatesRoot = input.templatesRoot ?? defaultStandardsTemplatesRoot();
2034
+ const now = input.now ?? /* @__PURE__ */ new Date();
2035
+ const lastReviewed = formatDate(now);
2036
+ const manifests = detectRuntimeManifests(input.options);
2037
+ const files = [];
2038
+ const languages = uniqueLanguages(manifests);
2039
+ for (const language of languages) {
2040
+ files.push(
2041
+ await renderStandardsTemplate({
2042
+ templatesRoot,
2043
+ templateRelPath: `coding-style-${language}.md.eta`,
2044
+ targetPath: `docs/03-\u5DE5\u7A0B\u89C4\u8303\u4E0E\u7814\u53D1\u57FA\u7840\u8BBE\u65BD/coding-style-${language}.md`,
2045
+ contributedBy: `standards:coding-style-${language}`,
2046
+ options: input.options,
2047
+ scaffolderVersion: input.scaffolderVersion,
2048
+ now,
2049
+ standards: { tier: input.tier, language, lastReviewed }
2050
+ })
2051
+ );
2052
+ }
2053
+ if (input.tier === "standard" || input.tier === "full") {
2054
+ for (const m of manifests) {
2055
+ const fileBaseName = `tech-stack-${m.env}.md`;
2056
+ files.push(
2057
+ await renderStandardsTemplate({
2058
+ templatesRoot,
2059
+ templateRelPath: `tech-stack-${m.env}.md.eta`,
2060
+ targetPath: `docs/03-\u5DE5\u7A0B\u89C4\u8303\u4E0E\u7814\u53D1\u57FA\u7840\u8BBE\u65BD/${fileBaseName}`,
2061
+ contributedBy: `standards:tech-stack-${m.env}`,
2062
+ options: input.options,
2063
+ scaffolderVersion: input.scaffolderVersion,
2064
+ now,
2065
+ standards: {
2066
+ tier: input.tier,
2067
+ env: m.env,
2068
+ language: m.language,
2069
+ packageManager: m.packageManager,
2070
+ manifestPath: m.manifestPath,
2071
+ lastReviewed,
2072
+ multiApp: input.options.multiApp
2073
+ // Smuggle dependencies through standards context so the
2074
+ // template can iterate. We attach via Object.assign-style
2075
+ // cast because StandardsRenderContext does not declare it
2076
+ // (deps are only read by the tech-stack templates and the
2077
+ // shape varies per env, so keeping it untyped here is
2078
+ // acceptable).
2079
+ },
2080
+ extraStandardsFields: { dependencies: m.dependencies }
2081
+ })
2082
+ );
2083
+ }
2084
+ files.push(
2085
+ await renderStandardsTemplate({
2086
+ templatesRoot,
2087
+ templateRelPath: "module-boundaries.md.eta",
2088
+ targetPath: "docs/03-\u5DE5\u7A0B\u89C4\u8303\u4E0E\u7814\u53D1\u57FA\u7840\u8BBE\u65BD/module-boundaries.md",
2089
+ contributedBy: "standards:module-boundaries",
2090
+ options: input.options,
2091
+ scaffolderVersion: input.scaffolderVersion,
2092
+ now,
2093
+ standards: {
2094
+ tier: input.tier,
2095
+ multiApp: input.options.multiApp,
2096
+ lastReviewed
2097
+ }
2098
+ })
2099
+ );
2100
+ if (input.options.multiApp && hasJsTsEnvironment(manifests)) {
2101
+ files.push(
2102
+ await renderStandardsTemplate({
2103
+ templatesRoot,
2104
+ templateRelPath: "dependency-cruiser.config.cjs.eta",
2105
+ targetPath: "dependency-cruiser.config.cjs",
2106
+ contributedBy: "standards:dep-cruiser",
2107
+ options: input.options,
2108
+ scaffolderVersion: input.scaffolderVersion,
2109
+ now,
2110
+ standards: {
2111
+ tier: input.tier,
2112
+ multiApp: true,
2113
+ lastReviewed
2114
+ }
2115
+ })
2116
+ );
2117
+ }
2118
+ }
2119
+ if (input.tier === "full" && hasFrontendSurface(input.options)) {
2120
+ files.push(
2121
+ await renderStandardsTemplate({
2122
+ templatesRoot,
2123
+ templateRelPath: "ui-design-system.md.eta",
2124
+ targetPath: "docs/05-\u524D\u7AEF\u5BA2\u6237\u7AEF\u8BE6\u7EC6\u8BBE\u8BA1/ui-design-system.md",
2125
+ contributedBy: "standards:ui-design-system",
2126
+ options: input.options,
2127
+ scaffolderVersion: input.scaffolderVersion,
2128
+ now,
2129
+ standards: { tier: "full", lastReviewed }
2130
+ })
2131
+ );
2132
+ if (input.options.frontend !== "none") {
2133
+ files.push(
2134
+ await renderStandardsTemplate({
2135
+ templatesRoot,
2136
+ templateRelPath: "design-tokens.ts.eta",
2137
+ targetPath: "web/src/design-tokens.ts",
2138
+ contributedBy: "standards:design-tokens",
2139
+ options: input.options,
2140
+ scaffolderVersion: input.scaffolderVersion,
2141
+ now,
2142
+ standards: { tier: "full", lastReviewed }
2143
+ })
2144
+ );
2145
+ }
2146
+ }
2147
+ const sortedFiles = [...files].sort(
2148
+ (a, b) => a.targetPath < b.targetPath ? -1 : a.targetPath > b.targetPath ? 1 : 0
2149
+ );
2150
+ const directories = collectDirectories(sortedFiles);
2151
+ return { files: sortedFiles, directories };
2152
+ }
2153
+ async function renderStandardsTemplate(input) {
2154
+ const templatePath = path6.join(input.templatesRoot, input.templateRelPath);
2155
+ const standards = {
2156
+ ...input.standards,
2157
+ ...input.extraStandardsFields ?? {}
2158
+ };
2159
+ const ctx = buildRenderContext({
2160
+ options: input.options,
2161
+ scaffolderVersion: input.scaffolderVersion,
2162
+ now: input.now,
2163
+ standards
2164
+ });
2165
+ let content;
2166
+ try {
2167
+ content = await renderFile(templatePath, ctx);
2168
+ } catch (e) {
2169
+ throw new TemplateError(
2170
+ `engineering-standards template render failed: ${input.templateRelPath}: ${e.message}`,
2171
+ "\u8BF7\u786E\u8BA4 src/standards/templates/ \u4E0B\u5B58\u5728 " + input.templateRelPath
2172
+ );
2173
+ }
2174
+ return {
2175
+ targetPath: input.targetPath,
2176
+ content,
2177
+ contributedBy: input.contributedBy
2178
+ };
2179
+ }
2180
+ function uniqueLanguages(manifests) {
2181
+ const seen = /* @__PURE__ */ new Set();
2182
+ const out = [];
2183
+ for (const m of manifests) {
2184
+ if (!seen.has(m.language)) {
2185
+ seen.add(m.language);
2186
+ out.push(m.language);
2187
+ }
2188
+ }
2189
+ return out;
2190
+ }
2191
+ function hasJsTsEnvironment(manifests) {
2192
+ return manifests.some((m) => m.language === "typescript");
2193
+ }
2194
+ function hasFrontendSurface(options) {
2195
+ if (options.frontend !== "none") return true;
2196
+ if (options.mobile === "react-native" || options.mobile === "flutter") return true;
2197
+ if (options.miniapp !== "none") return true;
2198
+ return false;
2199
+ }
2200
+ function formatDate(d) {
2201
+ const y = d.getUTCFullYear();
2202
+ const m = String(d.getUTCMonth() + 1).padStart(2, "0");
2203
+ const day = String(d.getUTCDate()).padStart(2, "0");
2204
+ return `${y}-${m}-${day}`;
2205
+ }
2206
+ function collectDirectories(files) {
2207
+ const set = /* @__PURE__ */ new Set();
2208
+ for (const f of files) {
2209
+ let cur = path6.posix.dirname(f.targetPath);
2210
+ while (cur && cur !== "." && cur !== "/") {
2211
+ set.add(cur);
2212
+ cur = path6.posix.dirname(cur);
2213
+ }
2214
+ }
2215
+ return [...set].sort();
2216
+ }
2217
+ function defaultStandardsTemplatesRoot() {
2218
+ const here = path6.dirname(fileURLToPath2(import.meta.url));
2219
+ const pkgRoot = findPackageRoot2(here);
2220
+ return path6.join(pkgRoot, "src", "standards", "templates");
2221
+ }
2222
+ function findPackageRoot2(start) {
2223
+ let dir = start;
2224
+ for (let i = 0; i < 10; i += 1) {
2225
+ const candidate = path6.join(dir, "package.json");
2226
+ try {
2227
+ accessSync2(candidate);
2228
+ return dir;
2229
+ } catch {
2230
+ }
2231
+ const parent = path6.dirname(dir);
2232
+ if (parent === dir) break;
2233
+ dir = parent;
2234
+ }
2235
+ throw new TemplateError(
2236
+ `unable to locate package.json above ${start}`,
2237
+ "\u8BF7\u786E\u8BA4 scaffolder \u5B89\u88C5\u5B8C\u6574\uFF0Csrc/standards/templates/ \u4E0E package.json \u4F4D\u4E8E\u540C\u4E00\u5C42\u7EA7"
2238
+ );
2239
+ }
2240
+
1698
2241
  // src/orchestrator.ts
1699
2242
  async function runCreate(input) {
1700
2243
  const reporter = new Reporter(
@@ -1722,17 +2265,34 @@ async function runCreate(input) {
1722
2265
  name: f.manifest.name,
1723
2266
  version: f.manifest.version
1724
2267
  }));
2268
+ if (input.flags.engineeringStandards !== void 0) {
2269
+ const standardsPlan = await buildStandardsPlan({
2270
+ options,
2271
+ tier: input.flags.engineeringStandards,
2272
+ scaffolderVersion: SCAFFOLDER_VERSION,
2273
+ ...input.io.now !== void 0 ? { now: input.io.now } : {}
2274
+ });
2275
+ plan = {
2276
+ files: [...plan.files, ...standardsPlan.files].sort(
2277
+ (a, b) => a.targetPath < b.targetPath ? -1 : a.targetPath > b.targetPath ? 1 : 0
2278
+ ),
2279
+ directories: [
2280
+ .../* @__PURE__ */ new Set([...plan.directories, ...standardsPlan.directories])
2281
+ ].sort()
2282
+ };
2283
+ }
1725
2284
  plan = appendScaffolderMetadata({
1726
2285
  plan,
1727
2286
  scaffolderVersion: SCAFFOLDER_VERSION,
1728
2287
  options,
1729
2288
  templateFragments: fragmentRefs,
2289
+ ...input.flags.engineeringStandards !== void 0 ? { engineeringStandards: input.flags.engineeringStandards } : {},
1730
2290
  ...input.io.now !== void 0 ? { now: input.io.now } : {}
1731
2291
  });
1732
2292
  validatePlanSyntax(plan);
1733
2293
  reporter.info(`planned ${plan.files.length} file(s) across ${plan.directories.length} directory`);
1734
2294
  const cwd = input.io.cwd ?? process.cwd();
1735
- const targetDirectory = path6.resolve(cwd, options.projectName);
2295
+ const targetDirectory = path7.resolve(cwd, options.projectName);
1736
2296
  if (input.flags.dryRun) {
1737
2297
  reporter.stage("dry-run");
1738
2298
  reporter.dryRunReport(plan);
@@ -1763,7 +2323,19 @@ async function resolveOptions(input) {
1763
2323
  ...merged,
1764
2324
  projectName,
1765
2325
  ...flags.ci !== void 0 ? { ci: flags.ci } : {},
1766
- ...flags.roles !== void 0 ? { roles: flags.roles } : {}
2326
+ ...flags.roles !== void 0 ? { roles: flags.roles } : {},
2327
+ ...flags.backend !== void 0 ? { backend: flags.backend } : {},
2328
+ ...flags.frontend !== void 0 ? { frontend: flags.frontend } : {},
2329
+ ...flags.mobile !== void 0 ? { mobile: flags.mobile } : {},
2330
+ ...flags.miniapp !== void 0 ? { miniapp: flags.miniapp } : {},
2331
+ ...flags.agent !== void 0 ? { agent: flags.agent } : {},
2332
+ ...flags.deploy !== void 0 ? { deploy: flags.deploy } : {},
2333
+ ...flags.contract !== void 0 ? { contract: flags.contract } : {},
2334
+ ...flags.ai !== void 0 ? { ai: flags.ai } : {},
2335
+ ...flags.license !== void 0 ? { license: flags.license } : {},
2336
+ ...flags.gitLfs !== void 0 ? { gitLfs: flags.gitLfs } : {},
2337
+ ...flags.integrations !== void 0 ? { integrations: flags.integrations } : {},
2338
+ ...flags.multiApp !== void 0 ? { multiApp: flags.multiApp } : {}
1767
2339
  };
1768
2340
  if (flags.yes) {
1769
2341
  const full = { ...buildDefaultOptions(projectName), ...merged };
@@ -1793,12 +2365,12 @@ async function resolveOptions(input) {
1793
2365
 
1794
2366
  // src/feature/orchestrator.ts
1795
2367
  import { promises as fs8 } from "node:fs";
1796
- import * as path9 from "node:path";
2368
+ import * as path10 from "node:path";
1797
2369
 
1798
2370
  // src/feature/plan.ts
1799
- import { promises as fs7, accessSync as accessSync2 } from "node:fs";
1800
- import * as path7 from "node:path";
1801
- import { fileURLToPath as fileURLToPath2 } from "node:url";
2371
+ import { promises as fs7, accessSync as accessSync3 } from "node:fs";
2372
+ import * as path8 from "node:path";
2373
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
1802
2374
 
1803
2375
  // src/feature/openapi-edit.ts
1804
2376
  import { isMap, isScalar, parseDocument, YAMLMap } from "yaml";
@@ -1892,7 +2464,7 @@ function slugToOperationId(slug) {
1892
2464
  // src/feature/plan.ts
1893
2465
  async function buildFeaturePlan(input) {
1894
2466
  const templatesRoot = input.templatesRoot ?? defaultFeatureTemplatesRoot();
1895
- const sourceRoot = input.source === "sample" ? path7.join(templatesRoot, "sample", input.slug) : templatesRoot;
2467
+ const sourceRoot = input.source === "sample" ? path8.join(templatesRoot, "sample", input.slug) : templatesRoot;
1896
2468
  const nextAdrNumber = await computeAdrNumberForSlug(input.cwd, input.slug);
1897
2469
  const renderContext = buildRenderContext({
1898
2470
  options: input.options,
@@ -1906,7 +2478,7 @@ async function buildFeaturePlan(input) {
1906
2478
  const artifact = LAYER_ARTIFACTS[layer];
1907
2479
  const targetPath = computeTargetPath(layer, input.slug, nextAdrNumber);
1908
2480
  outputs.set(layer, targetPath);
1909
- const templatePath = path7.join(sourceRoot, artifact.placeholderTemplate);
2481
+ const templatePath = path8.join(sourceRoot, artifact.placeholderTemplate);
1910
2482
  const content = await renderFeatureTemplate(templatePath, renderContext, layer, input.source);
1911
2483
  files.push({
1912
2484
  targetPath,
@@ -1918,7 +2490,7 @@ async function buildFeaturePlan(input) {
1918
2490
  let openapiAlreadyExisted;
1919
2491
  if (input.layers.includes("backend") && input.options.contract !== "none") {
1920
2492
  const apiPath = "contracts/openapi/api.yaml";
1921
- const apiAbs = path7.join(input.cwd, apiPath);
2493
+ const apiAbs = path8.join(input.cwd, apiPath);
1922
2494
  let currentText;
1923
2495
  try {
1924
2496
  currentText = await fs7.readFile(apiAbs, "utf8");
@@ -1990,7 +2562,7 @@ async function buildFeaturePlan(input) {
1990
2562
  const sortedFiles = [...files].sort(
1991
2563
  (a, b) => a.targetPath < b.targetPath ? -1 : a.targetPath > b.targetPath ? 1 : 0
1992
2564
  );
1993
- const directories = collectDirectories(sortedFiles);
2565
+ const directories = collectDirectories2(sortedFiles);
1994
2566
  return {
1995
2567
  files: sortedFiles,
1996
2568
  directories,
@@ -2017,7 +2589,7 @@ async function renderFeatureTemplate(templatePath, context, layer, source) {
2017
2589
  }
2018
2590
  var ADR_FILE_RE = /^adr-(\d{4,})-/;
2019
2591
  async function computeAdrNumberForSlug(cwd, slug) {
2020
- const dir = path7.join(cwd, "docs", "02-\u7CFB\u7EDF\u65B9\u6848\u4E0E\u67B6\u6784");
2592
+ const dir = path8.join(cwd, "docs", "02-\u7CFB\u7EDF\u65B9\u6848\u4E0E\u67B6\u6784");
2021
2593
  let entries;
2022
2594
  try {
2023
2595
  entries = await fs7.readdir(dir);
@@ -2042,32 +2614,32 @@ async function computeAdrNumberForSlug(cwd, slug) {
2042
2614
  function escapeRegex(s) {
2043
2615
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2044
2616
  }
2045
- function collectDirectories(files) {
2617
+ function collectDirectories2(files) {
2046
2618
  const set = /* @__PURE__ */ new Set();
2047
2619
  for (const f of files) {
2048
- let cur = path7.posix.dirname(f.targetPath);
2620
+ let cur = path8.posix.dirname(f.targetPath);
2049
2621
  while (cur && cur !== "." && cur !== "/") {
2050
2622
  set.add(cur);
2051
- cur = path7.posix.dirname(cur);
2623
+ cur = path8.posix.dirname(cur);
2052
2624
  }
2053
2625
  }
2054
2626
  return [...set].sort();
2055
2627
  }
2056
2628
  function defaultFeatureTemplatesRoot() {
2057
- const here = path7.dirname(fileURLToPath2(import.meta.url));
2058
- const pkgRoot = findPackageRoot2(here);
2059
- return path7.join(pkgRoot, "src", "feature", "templates");
2629
+ const here = path8.dirname(fileURLToPath3(import.meta.url));
2630
+ const pkgRoot = findPackageRoot3(here);
2631
+ return path8.join(pkgRoot, "src", "feature", "templates");
2060
2632
  }
2061
- function findPackageRoot2(start) {
2633
+ function findPackageRoot3(start) {
2062
2634
  let dir = start;
2063
2635
  for (let i = 0; i < 10; i += 1) {
2064
- const candidate = path7.join(dir, "package.json");
2636
+ const candidate = path8.join(dir, "package.json");
2065
2637
  try {
2066
- accessSync2(candidate);
2638
+ accessSync3(candidate);
2067
2639
  return dir;
2068
2640
  } catch {
2069
2641
  }
2070
- const parent = path7.dirname(dir);
2642
+ const parent = path8.dirname(dir);
2071
2643
  if (parent === dir) break;
2072
2644
  dir = parent;
2073
2645
  }
@@ -2078,11 +2650,11 @@ function findPackageRoot2(start) {
2078
2650
  }
2079
2651
 
2080
2652
  // src/feature/manifest-update.ts
2081
- import * as path8 from "node:path";
2653
+ import * as path9 from "node:path";
2082
2654
  async function appendFeatureSlug(projectDirectory, slug) {
2083
2655
  const manifest = await loadMetadataFile(projectDirectory);
2084
2656
  const next = withFeatureSlug(manifest, slug);
2085
- const metadataPath = path8.join(projectDirectory, METADATA_FILENAME);
2657
+ const metadataPath = path9.join(projectDirectory, METADATA_FILENAME);
2086
2658
  if (next === manifest) {
2087
2659
  return { updated: false, metadataPath };
2088
2660
  }
@@ -2115,7 +2687,7 @@ async function runFeatureAdd(input) {
2115
2687
  const code = e.code;
2116
2688
  if (code === "ENOENT") {
2117
2689
  throw new UserInputError(
2118
- `no .scaffolder.json found at ${path9.join(input.cwd, METADATA_FILENAME)}`,
2690
+ `no .scaffolder.json found at ${path10.join(input.cwd, METADATA_FILENAME)}`,
2119
2691
  "\u8BF7\u5728 keel \u9879\u76EE\u6839\u76EE\u5F55\u8FD0\u884C\uFF1B\u6216\u5148\u7528 create-keel create \u521B\u5EFA\u9879\u76EE"
2120
2692
  );
2121
2693
  }
@@ -2170,7 +2742,7 @@ async function splitPlanByExistence(plan, cwd, force) {
2170
2742
  const toWrite = [];
2171
2743
  const skipped = [];
2172
2744
  for (const file of plan.files) {
2173
- const exists = await fileExists(path9.join(cwd, file.targetPath));
2745
+ const exists = await fileExists(path10.join(cwd, file.targetPath));
2174
2746
  if (exists) {
2175
2747
  skipped.push(file.targetPath);
2176
2748
  } else {