@deepstorm/cli 0.10.2 → 0.11.1

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 (124) hide show
  1. package/README.md +8 -8
  2. package/dist/build-registry.js +11 -5
  3. package/dist/cli.js +1256 -634
  4. package/dist/mcp/code-hosting/github.json +14 -1
  5. package/dist/mcp/docs-reference/context7.json +1 -10
  6. package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-read/SKILL.md +8 -5
  7. package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-write/SKILL.md +10 -8
  8. package/dist/mcp-skills/deepstorm-mcp-figma-read/SKILL.md +21 -20
  9. package/dist/mcp-skills/deepstorm-mcp-github-read/SKILL.md +17 -17
  10. package/dist/mcp-skills/deepstorm-mcp-github-write/SKILL.md +20 -20
  11. package/dist/mcp-skills/deepstorm-mcp-jira-read/SKILL.md +8 -8
  12. package/dist/mcp-skills/deepstorm-mcp-jira-write/SKILL.md +13 -12
  13. package/dist/mcp-skills/deepstorm-mcp-playwright-read/SKILL.md +10 -10
  14. package/dist/registry.json +10 -0
  15. package/dist/skills/atoll-ops/SKILL.md +4 -0
  16. package/dist/skills/reef-commit/SKILL.md +9 -5
  17. package/dist/skills/reef-commit/scripts/branch-check.mjs +5 -11
  18. package/dist/skills/reef-commit/scripts/check-openspec-status.mjs +6 -12
  19. package/dist/skills/reef-commit/scripts/collect-git-context.mjs +13 -11
  20. package/dist/skills/reef-gen-backend/variants/java/steps.md +7 -7
  21. package/dist/skills/reef-gen-backend/variants/nodejs/steps.md +7 -7
  22. package/dist/skills/reef-gen-backend/variants/python/steps.md +7 -7
  23. package/dist/skills/reef-gen-frontend/variants/angular/steps.md +5 -5
  24. package/dist/skills/reef-gen-frontend/variants/react/steps.md +6 -6
  25. package/dist/skills/reef-gen-frontend/variants/vue/steps.md +6 -6
  26. package/dist/skills/reef-harden/EXAMPLES.md +12 -10
  27. package/dist/skills/reef-harden/SKILL.md +6 -0
  28. package/dist/skills/reef-harden/scripts/find-change-dir.mjs +13 -13
  29. package/dist/skills/reef-pr/SKILL.md +7 -0
  30. package/dist/skills/reef-pr/scripts/create-pr.mjs +31 -29
  31. package/dist/skills/reef-scope/SKILL.md +6 -6
  32. package/dist/skills/reef-start/references/jira-start-subagent.md +7 -7
  33. package/dist/skills/reef-start/references/risk-routing-card.md +28 -25
  34. package/dist/skills/reef-start/references/stage-4-implementation.md +33 -21
  35. package/dist/skills/reef-start/references/superpowers-gate.md +9 -9
  36. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/structured-output.md +2 -2
  37. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/quick-reference.md +11 -0
  38. package/dist/skills/reef-style-backend/fragments/java/api-spec/jackson-polymorphism.md +36 -40
  39. package/dist/skills/reef-style-backend/fragments/java/api-spec/quick-reference.md +11 -10
  40. package/dist/skills/reef-style-backend/fragments/java/db-migration/liquibase/examples/database-migration.md +7 -7
  41. package/dist/skills/reef-style-backend/fragments/java/dependency-management/quick-reference.md +24 -21
  42. package/dist/skills/reef-style-backend/fragments/java/exception-handling/examples/error-code-enum.md +7 -7
  43. package/dist/skills/reef-style-backend/fragments/java/exception-handling/quick-reference.md +10 -8
  44. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/dto-mapper.md +2 -2
  45. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/service-entity.md +5 -5
  46. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/testing.md +1 -0
  47. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/quick-reference.md +12 -12
  48. package/dist/skills/reef-style-backend/fragments/java/orm/hibernate/quick-reference.md +13 -12
  49. package/dist/skills/reef-style-backend/fragments/java/security-redlines/quick-reference.md +11 -9
  50. package/dist/skills/reef-style-backend/fragments/java/test/data-jpa-test/quick-reference.md +7 -7
  51. package/dist/skills/reef-style-backend/fragments/java/test/junit5/quick-reference.md +6 -6
  52. package/dist/skills/reef-style-backend/fragments/java/test/spring-mvc-test/quick-reference.md +8 -8
  53. package/dist/skills/reef-style-backend/fragments/java/test/spring-service-test/quick-reference.md +8 -7
  54. package/dist/skills/reef-style-backend/fragments/nodejs/eslint-config.json +1 -4
  55. package/dist/skills/reef-style-backend/fragments/nodejs/nestjs-structure.md +9 -9
  56. package/dist/skills/reef-style-backend/fragments/python/alembic-migration/quick-reference.md +2 -1
  57. package/dist/skills/reef-style-backend/fragments/python/api-spec/quick-reference.md +10 -10
  58. package/dist/skills/reef-style-backend/fragments/python/dependency-management/quick-reference.md +24 -22
  59. package/dist/skills/reef-style-backend/fragments/python/exception-handling/quick-reference.md +10 -9
  60. package/dist/skills/reef-style-backend/fragments/python/fastapi-quick-reference/quick-reference.md +3 -0
  61. package/dist/skills/reef-style-backend/fragments/python/langchain/quick-reference.md +10 -7
  62. package/dist/skills/reef-style-backend/fragments/python/pytest-testing/quick-reference.md +2 -0
  63. package/dist/skills/reef-style-backend/fragments/python/ruff-mypy-toolchain/quick-reference.md +2 -0
  64. package/dist/skills/reef-style-backend/fragments/python/security-redlines/quick-reference.md +11 -9
  65. package/dist/skills/reef-style-backend/fragments/python/sqlalchemy-orm/quick-reference.md +3 -0
  66. package/dist/skills/reef-style-backend/variants/java/examples/code-wrapping.md +3 -4
  67. package/dist/skills/reef-style-backend/variants/java/quick-reference.md +31 -29
  68. package/dist/skills/reef-style-backend/variants/nodejs/examples/module-example.md +2 -8
  69. package/dist/skills/reef-style-backend/variants/nodejs/quick-reference.md +35 -36
  70. package/dist/skills/reef-style-backend/variants/python/quick-reference.md +32 -31
  71. package/dist/skills/reef-style-frontend/fragments/css/tailwind/quick-reference.md +9 -7
  72. package/dist/skills/reef-style-frontend/fragments/test/vitest/examples/testing.md +9 -4
  73. package/dist/skills/reef-style-frontend/fragments/test/vitest/quick-reference.md +4 -4
  74. package/dist/skills/reef-style-frontend/fragments/test/vitest-react/examples/testing.md +135 -140
  75. package/dist/skills/reef-style-frontend/fragments/test/vitest-react/quick-reference.md +32 -32
  76. package/dist/skills/reef-style-frontend/fragments/test/vitest-vue/examples/testing.md +161 -162
  77. package/dist/skills/reef-style-frontend/fragments/test/vitest-vue/quick-reference.md +42 -42
  78. package/dist/skills/reef-style-frontend/fragments/ts-config/strict/quick-reference.md +16 -10
  79. package/dist/skills/reef-style-frontend/fragments/ui-lib/antd/examples/ui-components.md +72 -87
  80. package/dist/skills/reef-style-frontend/fragments/ui-lib/antd/quick-reference.md +14 -10
  81. package/dist/skills/reef-style-frontend/fragments/ui-lib/antd-vue/examples/ui-components.md +93 -111
  82. package/dist/skills/reef-style-frontend/fragments/ui-lib/antd-vue/quick-reference.md +29 -33
  83. package/dist/skills/reef-style-frontend/fragments/ui-lib/primeng/examples/ui-components.md +14 -23
  84. package/dist/skills/reef-style-frontend/variants/angular/examples/code-wrapping.md +15 -17
  85. package/dist/skills/reef-style-frontend/variants/angular/examples/component-types-pipes.md +22 -5
  86. package/dist/skills/reef-style-frontend/variants/angular/examples/entity-types.md +52 -22
  87. package/dist/skills/reef-style-frontend/variants/angular/examples/forms-layer.md +17 -20
  88. package/dist/skills/reef-style-frontend/variants/angular/examples/service-routing.md +8 -11
  89. package/dist/skills/reef-style-frontend/variants/angular/quick-reference.md +27 -27
  90. package/dist/skills/reef-style-frontend/variants/react/examples/code-wrapping.md +14 -27
  91. package/dist/skills/reef-style-frontend/variants/react/examples/component-types-pipes.md +45 -52
  92. package/dist/skills/reef-style-frontend/variants/react/examples/entity-types.md +44 -44
  93. package/dist/skills/reef-style-frontend/variants/react/examples/forms-layer.md +56 -85
  94. package/dist/skills/reef-style-frontend/variants/react/examples/service-routing.md +114 -88
  95. package/dist/skills/reef-style-frontend/variants/react/quick-reference.md +48 -42
  96. package/dist/skills/reef-style-frontend/variants/vue/examples/code-wrapping.md +24 -43
  97. package/dist/skills/reef-style-frontend/variants/vue/examples/component-types-pipes.md +48 -49
  98. package/dist/skills/reef-style-frontend/variants/vue/examples/entity-types.md +45 -45
  99. package/dist/skills/reef-style-frontend/variants/vue/examples/forms-layer.md +64 -68
  100. package/dist/skills/reef-style-frontend/variants/vue/examples/service-routing.md +85 -84
  101. package/dist/skills/reef-style-frontend/variants/vue/quick-reference.md +46 -51
  102. package/dist/skills/reef-testcase/SKILL.md +19 -19
  103. package/dist/skills/reef-testcase/references/coverage-dimensions.md +8 -0
  104. package/dist/skills/reef-testcase/references/test-case-template.md +16 -16
  105. package/dist/skills/sweep-explore/SKILL.md +824 -0
  106. package/dist/skills/sweep-explore/references/explore-flow-template.md +61 -0
  107. package/dist/skills/sweep-init/SKILL.md +4 -0
  108. package/dist/skills/sweep-init/scripts/flow-selector.mjs +6 -11
  109. package/dist/skills/sweep-init/scripts/init-project.mjs +68 -51
  110. package/dist/skills/sweep-plan/references/test-flow-template.md +12 -6
  111. package/dist/skills/sweep-record/SKILL.md +358 -0
  112. package/dist/skills/sweep-run/SKILL.md +59 -50
  113. package/dist/skills/sweep-run/scripts/env-manager.mjs +4 -5
  114. package/dist/skills/sweep-run/scripts/flow-parser.mjs +7 -6
  115. package/dist/skills/sweep-run/scripts/flow-selector.mjs +4 -15
  116. package/dist/skills/sweep-run/scripts/generate-report.mjs +1 -2
  117. package/dist/skills/sweep-run/scripts/mcp-manager.mjs +107 -14
  118. package/dist/skills/sweep-run/scripts/spec-compiler.mjs +19 -9
  119. package/dist/skills/tide-discuss/references/checklists.md +32 -32
  120. package/dist/skills/tide-discuss/references/data-format.md +85 -77
  121. package/dist/skills/tide-discuss/references/prd-template.md +21 -19
  122. package/dist/skills/tide-discuss/references/publish-flow.md +30 -11
  123. package/dist/skills/tide-discuss/references/role-prompts.md +5 -5
  124. package/package.json +5 -3
package/dist/cli.js CHANGED
@@ -1187,13 +1187,13 @@ var require_ast = __commonJS({
1187
1187
  helperExpression: function helperExpression(node) {
1188
1188
  return node.type === "SubExpression" || (node.type === "MustacheStatement" || node.type === "BlockStatement") && !!(node.params && node.params.length || node.hash);
1189
1189
  },
1190
- scopedId: function scopedId(path24) {
1191
- return /^\.|this\b/.test(path24.original);
1190
+ scopedId: function scopedId(path26) {
1191
+ return /^\.|this\b/.test(path26.original);
1192
1192
  },
1193
1193
  // an ID is simple if it only has one part, and that part is not
1194
1194
  // `..` or `this`.
1195
- simpleId: function simpleId(path24) {
1196
- return path24.parts.length === 1 && !AST.helpers.scopedId(path24) && !path24.depth;
1195
+ simpleId: function simpleId(path26) {
1196
+ return path26.parts.length === 1 && !AST.helpers.scopedId(path26) && !path26.depth;
1197
1197
  }
1198
1198
  }
1199
1199
  };
@@ -2263,12 +2263,12 @@ var require_helpers2 = __commonJS({
2263
2263
  loc
2264
2264
  };
2265
2265
  }
2266
- function prepareMustache(path24, params, hash, open, strip, locInfo) {
2266
+ function prepareMustache(path26, params, hash, open, strip, locInfo) {
2267
2267
  var escapeFlag = open.charAt(3) || open.charAt(2), escaped = escapeFlag !== "{" && escapeFlag !== "&";
2268
2268
  var decorator = /\*/.test(open);
2269
2269
  return {
2270
2270
  type: decorator ? "Decorator" : "MustacheStatement",
2271
- path: path24,
2271
+ path: path26,
2272
2272
  params,
2273
2273
  hash,
2274
2274
  escaped,
@@ -2586,9 +2586,9 @@ var require_compiler = __commonJS({
2586
2586
  },
2587
2587
  DecoratorBlock: function DecoratorBlock(decorator) {
2588
2588
  var program2 = decorator.program && this.compileProgram(decorator.program);
2589
- var params = this.setupFullMustacheParams(decorator, program2, void 0), path24 = decorator.path;
2589
+ var params = this.setupFullMustacheParams(decorator, program2, void 0), path26 = decorator.path;
2590
2590
  this.useDecorators = true;
2591
- this.opcode("registerDecorator", params.length, path24.original);
2591
+ this.opcode("registerDecorator", params.length, path26.original);
2592
2592
  },
2593
2593
  PartialStatement: function PartialStatement(partial) {
2594
2594
  this.usePartial = true;
@@ -2652,46 +2652,46 @@ var require_compiler = __commonJS({
2652
2652
  }
2653
2653
  },
2654
2654
  ambiguousSexpr: function ambiguousSexpr(sexpr, program2, inverse) {
2655
- var path24 = sexpr.path, name = path24.parts[0], isBlock = program2 != null || inverse != null;
2656
- this.opcode("getContext", path24.depth);
2655
+ var path26 = sexpr.path, name = path26.parts[0], isBlock = program2 != null || inverse != null;
2656
+ this.opcode("getContext", path26.depth);
2657
2657
  this.opcode("pushProgram", program2);
2658
2658
  this.opcode("pushProgram", inverse);
2659
- path24.strict = true;
2660
- this.accept(path24);
2659
+ path26.strict = true;
2660
+ this.accept(path26);
2661
2661
  this.opcode("invokeAmbiguous", name, isBlock);
2662
2662
  },
2663
2663
  simpleSexpr: function simpleSexpr(sexpr) {
2664
- var path24 = sexpr.path;
2665
- path24.strict = true;
2666
- this.accept(path24);
2664
+ var path26 = sexpr.path;
2665
+ path26.strict = true;
2666
+ this.accept(path26);
2667
2667
  this.opcode("resolvePossibleLambda");
2668
2668
  },
2669
2669
  helperSexpr: function helperSexpr(sexpr, program2, inverse) {
2670
- var params = this.setupFullMustacheParams(sexpr, program2, inverse), path24 = sexpr.path, name = path24.parts[0];
2670
+ var params = this.setupFullMustacheParams(sexpr, program2, inverse), path26 = sexpr.path, name = path26.parts[0];
2671
2671
  if (this.options.knownHelpers[name]) {
2672
2672
  this.opcode("invokeKnownHelper", params.length, name);
2673
2673
  } else if (this.options.knownHelpersOnly) {
2674
2674
  throw new _exception2["default"]("You specified knownHelpersOnly, but used the unknown helper " + name, sexpr);
2675
2675
  } else {
2676
- path24.strict = true;
2677
- path24.falsy = true;
2678
- this.accept(path24);
2679
- this.opcode("invokeHelper", params.length, path24.original, _ast2["default"].helpers.simpleId(path24));
2676
+ path26.strict = true;
2677
+ path26.falsy = true;
2678
+ this.accept(path26);
2679
+ this.opcode("invokeHelper", params.length, path26.original, _ast2["default"].helpers.simpleId(path26));
2680
2680
  }
2681
2681
  },
2682
- PathExpression: function PathExpression(path24) {
2683
- this.addDepth(path24.depth);
2684
- this.opcode("getContext", path24.depth);
2685
- var name = path24.parts[0], scoped = _ast2["default"].helpers.scopedId(path24), blockParamId = !path24.depth && !scoped && this.blockParamIndex(name);
2682
+ PathExpression: function PathExpression(path26) {
2683
+ this.addDepth(path26.depth);
2684
+ this.opcode("getContext", path26.depth);
2685
+ var name = path26.parts[0], scoped = _ast2["default"].helpers.scopedId(path26), blockParamId = !path26.depth && !scoped && this.blockParamIndex(name);
2686
2686
  if (blockParamId) {
2687
- this.opcode("lookupBlockParam", blockParamId, path24.parts);
2687
+ this.opcode("lookupBlockParam", blockParamId, path26.parts);
2688
2688
  } else if (!name) {
2689
2689
  this.opcode("pushContext");
2690
- } else if (path24.data) {
2690
+ } else if (path26.data) {
2691
2691
  this.options.data = true;
2692
- this.opcode("lookupData", path24.depth, path24.parts, path24.strict);
2692
+ this.opcode("lookupData", path26.depth, path26.parts, path26.strict);
2693
2693
  } else {
2694
- this.opcode("lookupOnContext", path24.parts, path24.falsy, path24.strict, scoped);
2694
+ this.opcode("lookupOnContext", path26.parts, path26.falsy, path26.strict, scoped);
2695
2695
  }
2696
2696
  },
2697
2697
  StringLiteral: function StringLiteral(string) {
@@ -3041,16 +3041,16 @@ var require_util = __commonJS({
3041
3041
  }
3042
3042
  exports2.urlGenerate = urlGenerate;
3043
3043
  function normalize(aPath) {
3044
- var path24 = aPath;
3044
+ var path26 = aPath;
3045
3045
  var url = urlParse(aPath);
3046
3046
  if (url) {
3047
3047
  if (!url.path) {
3048
3048
  return aPath;
3049
3049
  }
3050
- path24 = url.path;
3050
+ path26 = url.path;
3051
3051
  }
3052
- var isAbsolute = exports2.isAbsolute(path24);
3053
- var parts = path24.split(/\/+/);
3052
+ var isAbsolute = exports2.isAbsolute(path26);
3053
+ var parts = path26.split(/\/+/);
3054
3054
  for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {
3055
3055
  part = parts[i];
3056
3056
  if (part === ".") {
@@ -3067,18 +3067,18 @@ var require_util = __commonJS({
3067
3067
  }
3068
3068
  }
3069
3069
  }
3070
- path24 = parts.join("/");
3071
- if (path24 === "") {
3072
- path24 = isAbsolute ? "/" : ".";
3070
+ path26 = parts.join("/");
3071
+ if (path26 === "") {
3072
+ path26 = isAbsolute ? "/" : ".";
3073
3073
  }
3074
3074
  if (url) {
3075
- url.path = path24;
3075
+ url.path = path26;
3076
3076
  return urlGenerate(url);
3077
3077
  }
3078
- return path24;
3078
+ return path26;
3079
3079
  }
3080
3080
  exports2.normalize = normalize;
3081
- function join22(aRoot, aPath) {
3081
+ function join24(aRoot, aPath) {
3082
3082
  if (aRoot === "") {
3083
3083
  aRoot = ".";
3084
3084
  }
@@ -3110,7 +3110,7 @@ var require_util = __commonJS({
3110
3110
  }
3111
3111
  return joined;
3112
3112
  }
3113
- exports2.join = join22;
3113
+ exports2.join = join24;
3114
3114
  exports2.isAbsolute = function(aPath) {
3115
3115
  return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
3116
3116
  };
@@ -3283,7 +3283,7 @@ var require_util = __commonJS({
3283
3283
  parsed.path = parsed.path.substring(0, index + 1);
3284
3284
  }
3285
3285
  }
3286
- sourceURL = join22(urlGenerate(parsed), sourceURL);
3286
+ sourceURL = join24(urlGenerate(parsed), sourceURL);
3287
3287
  }
3288
3288
  return normalize(sourceURL);
3289
3289
  }
@@ -5858,8 +5858,8 @@ var require_printer = __commonJS({
5858
5858
  return this.accept(sexpr.path) + " " + params + hash;
5859
5859
  };
5860
5860
  PrintVisitor.prototype.PathExpression = function(id) {
5861
- var path24 = id.parts.join("/");
5862
- return (id.data ? "@" : "") + "PATH:" + path24;
5861
+ var path26 = id.parts.join("/");
5862
+ return (id.data ? "@" : "") + "PATH:" + path26;
5863
5863
  };
5864
5864
  PrintVisitor.prototype.StringLiteral = function(string) {
5865
5865
  return '"' + string.value + '"';
@@ -5898,8 +5898,8 @@ var require_lib = __commonJS({
5898
5898
  handlebars.print = printer.print;
5899
5899
  module2.exports = handlebars;
5900
5900
  function extension(module3, filename) {
5901
- var fs28 = require("fs");
5902
- var templateString = fs28.readFileSync(filename, "utf8");
5901
+ var fs30 = require("fs");
5902
+ var templateString = fs30.readFileSync(filename, "utf8");
5903
5903
  module3.exports = handlebars.compile(templateString);
5904
5904
  }
5905
5905
  if (typeof require !== "undefined" && require.extensions) {
@@ -5911,8 +5911,8 @@ var require_lib = __commonJS({
5911
5911
 
5912
5912
  // src/index.ts
5913
5913
  var import_commander = require("commander");
5914
- var fs27 = __toESM(require("node:fs"));
5915
- var path23 = __toESM(require("node:path"));
5914
+ var fs29 = __toESM(require("node:fs"));
5915
+ var path25 = __toESM(require("node:path"));
5916
5916
 
5917
5917
  // src/commands/setup.ts
5918
5918
  var path9 = __toESM(require("node:path"));
@@ -5931,9 +5931,7 @@ var RegistryReader = class {
5931
5931
  }
5932
5932
  /** 按 configKey + configValue 匹配 skill,返回 skill ID 列表 */
5933
5933
  findSkillIds(configKey, configValue) {
5934
- return Object.entries(this.registry.skills).filter(
5935
- ([, s]) => s.configKey === configKey && s.configValue === configValue
5936
- ).map(([id]) => id);
5934
+ return Object.entries(this.registry.skills).filter(([, s]) => s.configKey === configKey && s.configValue === configValue).map(([id]) => id);
5937
5935
  }
5938
5936
  /** 获取所有工具名称列表 */
5939
5937
  getTools() {
@@ -5953,9 +5951,7 @@ var RegistryReader = class {
5953
5951
  }
5954
5952
  /** 获取指定工具的所有 skill */
5955
5953
  getToolSkills(toolName) {
5956
- return Object.values(this.registry.skills).filter(
5957
- (s) => s.tool === toolName
5958
- );
5954
+ return Object.values(this.registry.skills).filter((s) => s.tool === toolName);
5959
5955
  }
5960
5956
  /** 获取指定工具的 agent 文件名列表 */
5961
5957
  getToolAgents(toolName) {
@@ -6001,10 +5997,7 @@ function deepMerge(target, source) {
6001
5997
  const val = source[key];
6002
5998
  const existing = result[key];
6003
5999
  if (isPlainObject(existing) && isPlainObject(val)) {
6004
- result[key] = deepMerge(
6005
- existing,
6006
- val
6007
- );
6000
+ result[key] = deepMerge(existing, val);
6008
6001
  } else {
6009
6002
  result[key] = val;
6010
6003
  }
@@ -6232,7 +6225,10 @@ function initContextMap(targetDir, opts) {
6232
6225
  ensureDir2(deepstormDir);
6233
6226
  const backendLang = opts.backend || "none";
6234
6227
  const frontendFramework = opts.frontend || "none";
6235
- const content = CONTEXT_MD_TEMPLATE.replace("{backend.language}", backendLang).replace("{frontend.framework}", frontendFramework);
6228
+ const content = CONTEXT_MD_TEMPLATE.replace("{backend.language}", backendLang).replace(
6229
+ "{frontend.framework}",
6230
+ frontendFramework
6231
+ );
6236
6232
  fs2.writeFileSync(contextPath, content, "utf-8");
6237
6233
  console.log("\u2714 \u5DF2\u521B\u5EFA .deepstorm/context.md \u9879\u76EE\u4E0A\u4E0B\u6587\u5730\u56FE");
6238
6234
  }
@@ -6242,10 +6238,7 @@ function initClaudeMd(targetDir, opts) {
6242
6238
  if (fs2.existsSync(claudeMdPath)) return;
6243
6239
  ensureDir2(claudeDir);
6244
6240
  const projectName = opts.projectName || "unknown";
6245
- const lines = [
6246
- `# ${projectName}`,
6247
- ""
6248
- ];
6241
+ const lines = [`# ${projectName}`, ""];
6249
6242
  const techLines = [];
6250
6243
  if (opts.frontend) {
6251
6244
  techLines.push(`- **\u524D\u7AEF**\uFF1A${opts.frontend}`);
@@ -6342,7 +6335,6 @@ async function promptContinueSetup(targetDir, projectName, registry2) {
6342
6335
  if (!registry2) return;
6343
6336
  if (!registry2.tools || Object.keys(registry2.tools).length === 0) return;
6344
6337
  const { confirm: confirm6, isCancel: isCancel8, outro: outro2 } = await import("@clack/prompts");
6345
- const projectPath = path2.join(targetDir, projectName);
6346
6338
  const shouldContinue = await confirm6({
6347
6339
  message: `\u662F\u5426\u7EE7\u7EED\u5B89\u88C5 DeepStorm \u5F00\u53D1\u73AF\u5883\uFF08skills + MCP \u670D\u52A1\uFF09\uFF1F`,
6348
6340
  initialValue: true
@@ -6568,7 +6560,6 @@ async function runInit(baseDir, opts) {
6568
6560
  const hasFrontend = opts.frontend === "angular";
6569
6561
  const hasJavaBackend = opts.backend === "java";
6570
6562
  const hasNodeBackend = opts.backend === "nodejs";
6571
- const hasBackend = hasJavaBackend || hasNodeBackend;
6572
6563
  try {
6573
6564
  const isMonorepo = hasFrontend && hasNodeBackend;
6574
6565
  if (isMonorepo) {
@@ -6618,89 +6609,130 @@ function renderAngularTemplate(projectDir, ctx, subPkg = false) {
6618
6609
  const hasPrimeng = ctx.uiLib === "primeng";
6619
6610
  const hasTailwind = ctx.cssFramework === "tailwind";
6620
6611
  const webRoot = subPkg ? "src" : "src/main/web";
6621
- writeTemplate(projectDir, "angular.json", JSON.stringify({
6622
- $schema: "./node_modules/@angular/cli/lib/config/schema.json",
6623
- version: 1,
6624
- newProjectRoot: "projects",
6625
- projects: {
6626
- [ctx.packageName]: {
6627
- projectType: "application",
6628
- root: "",
6629
- sourceRoot: webRoot,
6630
- prefix: "app",
6631
- architect: {
6632
- build: {
6633
- builder: "@angular-devkit/build-angular:application",
6634
- options: {
6635
- outputPath: "build",
6636
- index: `${webRoot}/index.html`,
6637
- browser: `${webRoot}/main.ts`,
6638
- polyfills: ["zone.js"],
6639
- tsConfig: "tsconfig.app.json",
6640
- assets: [{ glob: "**/*", input: "public" }],
6641
- styles: getAngularStyles(hasPrimeng, webRoot),
6642
- scripts: []
6643
- },
6644
- configurations: {
6645
- production: { budgets: [], outputHashing: "all" },
6646
- development: { optimization: false, extractLicenses: false, sourceMap: true }
6647
- },
6648
- defaultConfiguration: "production"
6649
- },
6650
- serve: {
6651
- builder: "@angular-devkit/build-angular:dev-server",
6652
- configurations: { production: { buildTarget: `${ctx.packageName}:build:production` }, development: { buildTarget: `${ctx.packageName}:build:development` } },
6653
- defaultConfiguration: "development",
6654
- options: { proxyConfig: "proxy.conf.json" }
6655
- },
6656
- test: {
6657
- builder: "@angular-devkit/build-angular:karma",
6658
- options: { polyfills: ["zone.js", "zone.js/testing"], tsConfig: "tsconfig.spec.json", assets: [{ glob: "**/*", input: "public" }], styles: getAngularStyles(hasPrimeng, webRoot), scripts: [] }
6612
+ writeTemplate(
6613
+ projectDir,
6614
+ "angular.json",
6615
+ JSON.stringify(
6616
+ {
6617
+ $schema: "./node_modules/@angular/cli/lib/config/schema.json",
6618
+ version: 1,
6619
+ newProjectRoot: "projects",
6620
+ projects: {
6621
+ [ctx.packageName]: {
6622
+ projectType: "application",
6623
+ root: "",
6624
+ sourceRoot: webRoot,
6625
+ prefix: "app",
6626
+ architect: {
6627
+ build: {
6628
+ builder: "@angular-devkit/build-angular:application",
6629
+ options: {
6630
+ outputPath: "build",
6631
+ index: `${webRoot}/index.html`,
6632
+ browser: `${webRoot}/main.ts`,
6633
+ polyfills: ["zone.js"],
6634
+ tsConfig: "tsconfig.app.json",
6635
+ assets: [{ glob: "**/*", input: "public" }],
6636
+ styles: getAngularStyles(hasPrimeng, webRoot),
6637
+ scripts: []
6638
+ },
6639
+ configurations: {
6640
+ production: { budgets: [], outputHashing: "all" },
6641
+ development: { optimization: false, extractLicenses: false, sourceMap: true }
6642
+ },
6643
+ defaultConfiguration: "production"
6644
+ },
6645
+ serve: {
6646
+ builder: "@angular-devkit/build-angular:dev-server",
6647
+ configurations: {
6648
+ production: { buildTarget: `${ctx.packageName}:build:production` },
6649
+ development: { buildTarget: `${ctx.packageName}:build:development` }
6650
+ },
6651
+ defaultConfiguration: "development",
6652
+ options: { proxyConfig: "proxy.conf.json" }
6653
+ },
6654
+ test: {
6655
+ builder: "@angular-devkit/build-angular:karma",
6656
+ options: {
6657
+ polyfills: ["zone.js", "zone.js/testing"],
6658
+ tsConfig: "tsconfig.spec.json",
6659
+ assets: [{ glob: "**/*", input: "public" }],
6660
+ styles: getAngularStyles(hasPrimeng, webRoot),
6661
+ scripts: []
6662
+ }
6663
+ }
6664
+ }
6659
6665
  }
6660
6666
  }
6661
- }
6662
- }
6663
- }, null, 2));
6664
- writeTemplate(projectDir, "tsconfig.json", JSON.stringify({
6665
- compileOnSave: false,
6666
- compilerOptions: {
6667
- baseUrl: "./",
6668
- outDir: "./dist",
6669
- forceConsistentCasingInFileNames: true,
6670
- strict: true,
6671
- noImplicitOverride: true,
6672
- noPropertyAccessFromIndexSignature: true,
6673
- noImplicitReturns: true,
6674
- noFallthroughCasesInSwitch: true,
6675
- sourceMap: true,
6676
- declaration: false,
6677
- downlevelIteration: true,
6678
- experimentalDecorators: true,
6679
- moduleResolution: "bundler",
6680
- importHelpers: true,
6681
- target: "ES2022",
6682
- module: "ES2022",
6683
- lib: ["ES2022", "dom"],
6684
- skipLibCheck: true
6685
- },
6686
- angularCompilerOptions: {
6687
- enableI18nLegacyMessageIdFormat: false,
6688
- strictInjectionParameters: true,
6689
- strictInputAccessModifiers: true,
6690
- strictTemplates: true
6691
- }
6692
- }, null, 2));
6693
- writeTemplate(projectDir, "tsconfig.app.json", JSON.stringify({
6694
- extends: "./tsconfig.json",
6695
- compilerOptions: { outDir: "./out-tsc/app", types: [] },
6696
- files: [`${webRoot}/main.ts`],
6697
- include: ["src/**/*.d.ts"]
6698
- }, null, 2));
6699
- writeTemplate(projectDir, "tsconfig.spec.json", JSON.stringify({
6700
- extends: "./tsconfig.json",
6701
- compilerOptions: { outDir: "./out-tsc/spec", types: ["jasmine"] },
6702
- include: ["src/**/*.spec.ts", "src/**/*.d.ts"]
6703
- }, null, 2));
6667
+ },
6668
+ null,
6669
+ 2
6670
+ )
6671
+ );
6672
+ writeTemplate(
6673
+ projectDir,
6674
+ "tsconfig.json",
6675
+ JSON.stringify(
6676
+ {
6677
+ compileOnSave: false,
6678
+ compilerOptions: {
6679
+ baseUrl: "./",
6680
+ outDir: "./dist",
6681
+ forceConsistentCasingInFileNames: true,
6682
+ strict: true,
6683
+ noImplicitOverride: true,
6684
+ noPropertyAccessFromIndexSignature: true,
6685
+ noImplicitReturns: true,
6686
+ noFallthroughCasesInSwitch: true,
6687
+ sourceMap: true,
6688
+ declaration: false,
6689
+ downlevelIteration: true,
6690
+ experimentalDecorators: true,
6691
+ moduleResolution: "bundler",
6692
+ importHelpers: true,
6693
+ target: "ES2022",
6694
+ module: "ES2022",
6695
+ lib: ["ES2022", "dom"],
6696
+ skipLibCheck: true
6697
+ },
6698
+ angularCompilerOptions: {
6699
+ enableI18nLegacyMessageIdFormat: false,
6700
+ strictInjectionParameters: true,
6701
+ strictInputAccessModifiers: true,
6702
+ strictTemplates: true
6703
+ }
6704
+ },
6705
+ null,
6706
+ 2
6707
+ )
6708
+ );
6709
+ writeTemplate(
6710
+ projectDir,
6711
+ "tsconfig.app.json",
6712
+ JSON.stringify(
6713
+ {
6714
+ extends: "./tsconfig.json",
6715
+ compilerOptions: { outDir: "./out-tsc/app", types: [] },
6716
+ files: [`${webRoot}/main.ts`],
6717
+ include: ["src/**/*.d.ts"]
6718
+ },
6719
+ null,
6720
+ 2
6721
+ )
6722
+ );
6723
+ writeTemplate(
6724
+ projectDir,
6725
+ "tsconfig.spec.json",
6726
+ JSON.stringify(
6727
+ {
6728
+ extends: "./tsconfig.json",
6729
+ compilerOptions: { outDir: "./out-tsc/spec", types: ["jasmine"] },
6730
+ include: ["src/**/*.spec.ts", "src/**/*.d.ts"]
6731
+ },
6732
+ null,
6733
+ 2
6734
+ )
6735
+ );
6704
6736
  const pkgDeps = {
6705
6737
  "@angular/animations": "^21.0.0",
6706
6738
  "@angular/common": "^21.0.0",
@@ -6709,45 +6741,64 @@ function renderAngularTemplate(projectDir, ctx, subPkg = false) {
6709
6741
  "@angular/forms": "^21.0.0",
6710
6742
  "@angular/platform-browser": "^21.0.0",
6711
6743
  "@angular/router": "^21.0.0",
6712
- "rxjs": "~7.8.0",
6713
- "tslib": "^2.3.0",
6744
+ rxjs: "~7.8.0",
6745
+ tslib: "^2.3.0",
6714
6746
  "zone.js": "~0.15.0"
6715
6747
  };
6716
6748
  const devDeps = {
6717
6749
  "@angular-devkit/build-angular": "^21.0.0",
6718
6750
  "@angular/cli": "^21.0.0",
6719
6751
  "@angular/compiler-cli": "^21.0.0",
6720
- "typescript": "~5.7.0"
6752
+ typescript: "~5.7.0"
6721
6753
  };
6722
6754
  if (hasPrimeng) {
6723
6755
  pkgDeps["primeng"] = "^21.0.0";
6724
6756
  pkgDeps["@primeng/themes"] = "^21.0.0";
6725
6757
  pkgDeps["@primeuix/themes"] = "^2.0.0";
6726
6758
  }
6727
- writeTemplate(projectDir, "package.json", JSON.stringify({
6728
- name: ctx.packageName,
6729
- version: "1.0.0-dev",
6730
- private: true,
6731
- packageManager: "pnpm@10.0.0",
6732
- scripts: {
6733
- ng: "ng",
6734
- start: "ng serve",
6735
- build: "ng build",
6736
- watch: "ng build --watch --configuration development",
6737
- test: "ng test",
6738
- lint: "ng lint",
6739
- typecheck: "tsc --build --noEmit"
6740
- },
6741
- dependencies: pkgDeps,
6742
- devDependencies: devDeps
6743
- }, null, 2));
6744
- writeTemplate(projectDir, "proxy.conf.json", JSON.stringify({
6745
- "/api": {
6746
- target: "http://localhost:8080",
6747
- secure: false
6748
- }
6749
- }, null, 2));
6750
- writeTemplate(projectDir, "eslint.config.mjs", `import globals from "globals";
6759
+ writeTemplate(
6760
+ projectDir,
6761
+ "package.json",
6762
+ JSON.stringify(
6763
+ {
6764
+ name: ctx.packageName,
6765
+ version: "1.0.0-dev",
6766
+ private: true,
6767
+ packageManager: "pnpm@10.0.0",
6768
+ scripts: {
6769
+ ng: "ng",
6770
+ start: "ng serve",
6771
+ build: "ng build",
6772
+ watch: "ng build --watch --configuration development",
6773
+ test: "ng test",
6774
+ lint: "ng lint",
6775
+ typecheck: "tsc --build --noEmit"
6776
+ },
6777
+ dependencies: pkgDeps,
6778
+ devDependencies: devDeps
6779
+ },
6780
+ null,
6781
+ 2
6782
+ )
6783
+ );
6784
+ writeTemplate(
6785
+ projectDir,
6786
+ "proxy.conf.json",
6787
+ JSON.stringify(
6788
+ {
6789
+ "/api": {
6790
+ target: "http://localhost:8080",
6791
+ secure: false
6792
+ }
6793
+ },
6794
+ null,
6795
+ 2
6796
+ )
6797
+ );
6798
+ writeTemplate(
6799
+ projectDir,
6800
+ "eslint.config.mjs",
6801
+ `import globals from "globals";
6751
6802
  import pluginJs from "@eslint/js";
6752
6803
  import tseslint from "typescript-eslint";
6753
6804
 
@@ -6757,10 +6808,14 @@ export default [
6757
6808
  pluginJs.configs.recommended,
6758
6809
  ...tseslint.configs.recommended,
6759
6810
  ];
6760
- `);
6811
+ `
6812
+ );
6761
6813
  ensureDir2(path2.join(projectDir, "public"));
6762
6814
  ensureDir2(path2.join(projectDir, webRoot, "app"));
6763
- writeTemplate(projectDir, `${webRoot}/index.html`, `<!doctype html>
6815
+ writeTemplate(
6816
+ projectDir,
6817
+ `${webRoot}/index.html`,
6818
+ `<!doctype html>
6764
6819
  <html lang="zh-CN">
6765
6820
  <head>
6766
6821
  <meta charset="utf-8">
@@ -6773,20 +6828,28 @@ export default [
6773
6828
  <app-root></app-root>
6774
6829
  </body>
6775
6830
  </html>
6776
- `);
6777
- writeTemplate(projectDir, `${webRoot}/main.ts`, `import { bootstrapApplication } from '@angular/platform-browser';
6831
+ `
6832
+ );
6833
+ writeTemplate(
6834
+ projectDir,
6835
+ `${webRoot}/main.ts`,
6836
+ `import { bootstrapApplication } from '@angular/platform-browser';
6778
6837
  import { appConfig } from './app/app.config';
6779
6838
  import { App } from './app/app';
6780
6839
 
6781
6840
  bootstrapApplication(App, appConfig).catch((err) => console.error(err));
6782
- `);
6841
+ `
6842
+ );
6783
6843
  const stylesContent = hasTailwind ? `@import "tailwindcss";
6784
6844
 
6785
6845
  /* \u5168\u5C40\u6837\u5F0F */
6786
6846
  ` : `/* \u5168\u5C40\u6837\u5F0F */
6787
6847
  `;
6788
6848
  writeTemplate(projectDir, `${webRoot}/styles.css`, stylesContent);
6789
- writeTemplate(projectDir, `${webRoot}/app/app.ts`, `import { Component } from '@angular/core';
6849
+ writeTemplate(
6850
+ projectDir,
6851
+ `${webRoot}/app/app.ts`,
6852
+ `import { Component } from '@angular/core';
6790
6853
  import { RouterOutlet } from '@angular/router';
6791
6854
 
6792
6855
  @Component({
@@ -6796,8 +6859,12 @@ import { RouterOutlet } from '@angular/router';
6796
6859
  template: \`<router-outlet></router-outlet>\`,
6797
6860
  })
6798
6861
  export class App {}
6799
- `);
6800
- writeTemplate(projectDir, `${webRoot}/app/app.config.ts`, `import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
6862
+ `
6863
+ );
6864
+ writeTemplate(
6865
+ projectDir,
6866
+ `${webRoot}/app/app.config.ts`,
6867
+ `import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
6801
6868
  import { provideRouter } from '@angular/router';
6802
6869
  import { routes } from './app.routes';
6803
6870
 
@@ -6807,15 +6874,28 @@ export const appConfig: ApplicationConfig = {
6807
6874
  provideRouter(routes),
6808
6875
  ],
6809
6876
  };
6810
- `);
6811
- writeTemplate(projectDir, `${webRoot}/app/app.routes.ts`, `import { Routes } from '@angular/router';
6877
+ `
6878
+ );
6879
+ writeTemplate(
6880
+ projectDir,
6881
+ `${webRoot}/app/app.routes.ts`,
6882
+ `import { Routes } from '@angular/router';
6812
6883
 
6813
6884
  export const routes: Routes = [];
6814
- `);
6885
+ `
6886
+ );
6815
6887
  if (hasTailwind) {
6816
- writeTemplate(projectDir, "postcss.config.json", JSON.stringify({
6817
- plugins: { "@tailwindcss/postcss": {} }
6818
- }, null, 2));
6888
+ writeTemplate(
6889
+ projectDir,
6890
+ "postcss.config.json",
6891
+ JSON.stringify(
6892
+ {
6893
+ plugins: { "@tailwindcss/postcss": {} }
6894
+ },
6895
+ null,
6896
+ 2
6897
+ )
6898
+ );
6819
6899
  }
6820
6900
  }
6821
6901
  function getAngularStyles(hasPrimeng, webRoot) {
@@ -6827,40 +6907,56 @@ function getAngularStyles(hasPrimeng, webRoot) {
6827
6907
  return styles;
6828
6908
  }
6829
6909
  function renderMonorepoRoot(projectDir, ctx) {
6830
- writeTemplate(projectDir, "package.json", JSON.stringify({
6831
- name: ctx.packageName,
6832
- version: "0.1.0",
6833
- private: true,
6834
- description: "",
6835
- scripts: {
6836
- dev: "pnpm --filter server dev & pnpm --filter client dev",
6837
- build: "pnpm --filter server build",
6838
- lint: "pnpm --filter server lint",
6839
- format: "pnpm --filter server format",
6840
- test: "pnpm --filter server test"
6841
- }
6842
- }, null, 2));
6843
- writeTemplate(projectDir, "pnpm-workspace.yaml", `packages:
6910
+ writeTemplate(
6911
+ projectDir,
6912
+ "package.json",
6913
+ JSON.stringify(
6914
+ {
6915
+ name: ctx.packageName,
6916
+ version: "0.1.0",
6917
+ private: true,
6918
+ description: "",
6919
+ scripts: {
6920
+ dev: "pnpm --filter server dev & pnpm --filter client dev",
6921
+ build: "pnpm --filter server build",
6922
+ lint: "pnpm --filter server lint",
6923
+ format: "pnpm --filter server format",
6924
+ test: "pnpm --filter server test"
6925
+ }
6926
+ },
6927
+ null,
6928
+ 2
6929
+ )
6930
+ );
6931
+ writeTemplate(
6932
+ projectDir,
6933
+ "pnpm-workspace.yaml",
6934
+ `packages:
6844
6935
  - "server"
6845
6936
  - "client"
6846
- `);
6937
+ `
6938
+ );
6847
6939
  }
6848
6940
  function renderJavaTemplate(projectDir, ctx) {
6849
6941
  const hasOrm = ctx.orm === "hibernate";
6850
6942
  const hasLiquibase = ctx.migration === "liquibase";
6851
6943
  const hasSpringAi = ctx.ai === "spring-ai";
6852
- writeTemplate(projectDir, "settings.gradle.kts", `rootProject.name = "${ctx.packageName}"
6853
- `);
6854
- writeTemplate(projectDir, "gradle.properties", `# Gradle JVM \u53C2\u6570
6944
+ writeTemplate(
6945
+ projectDir,
6946
+ "settings.gradle.kts",
6947
+ `rootProject.name = "${ctx.packageName}"
6948
+ `
6949
+ );
6950
+ writeTemplate(
6951
+ projectDir,
6952
+ "gradle.properties",
6953
+ `# Gradle JVM \u53C2\u6570
6855
6954
  org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
6856
6955
  # \u5E76\u884C\u6784\u5EFA
6857
6956
  org.gradle.parallel=true
6858
- `);
6859
- const plugins = [
6860
- "java",
6861
- "org.springframework.boot",
6862
- "io.spring.dependency-management"
6863
- ];
6957
+ `
6958
+ );
6959
+ const plugins = ["java", "org.springframework.boot", "io.spring.dependency-management"];
6864
6960
  const gradleDeps = [
6865
6961
  'implementation("org.springframework.boot:spring-boot-starter-web")',
6866
6962
  'implementation("org.springframework.boot:spring-boot-starter-validation")',
@@ -6877,7 +6973,10 @@ org.gradle.parallel=true
6877
6973
  gradleDeps.push('implementation("org.springframework.ai:spring-ai-openai-spring-boot-starter")');
6878
6974
  }
6879
6975
  const reposExtra = hasSpringAi ? '\n maven { url = uri("https://repo.spring.io/milestone") }' : "";
6880
- writeTemplate(projectDir, "build.gradle.kts", `plugins {
6976
+ writeTemplate(
6977
+ projectDir,
6978
+ "build.gradle.kts",
6979
+ `plugins {
6881
6980
  ${plugins.map((p7) => `id("${p7}") version "3.4.0"`).join("\n ")}${hasLiquibase ? `
6882
6981
  id("org.liquibase.gradle") version "2.2.2"` : ""}
6883
6982
  }
@@ -6906,11 +7005,15 @@ dependencies {
6906
7005
  tasks.withType<Test> {
6907
7006
  useJUnitPlatform()
6908
7007
  }
6909
- `);
7008
+ `
7009
+ );
6910
7010
  const appPackage = ctx.groupId.replace(/[.-]/g, ".");
6911
7011
  const appDir = `src/main/java/${appPackage.replace(/\./g, "/")}`;
6912
7012
  ensureDir2(path2.join(projectDir, appDir));
6913
- writeTemplate(projectDir, `${appDir}/Application.java`, `package ${appPackage};
7013
+ writeTemplate(
7014
+ projectDir,
7015
+ `${appDir}/Application.java`,
7016
+ `package ${appPackage};
6914
7017
 
6915
7018
  import org.springframework.boot.SpringApplication;
6916
7019
  import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -6922,10 +7025,14 @@ public class Application {
6922
7025
  SpringApplication.run(Application.class, args);
6923
7026
  }
6924
7027
  }
6925
- `);
7028
+ `
7029
+ );
6926
7030
  const controllerDir = `${appDir}/controller`;
6927
7031
  ensureDir2(path2.join(projectDir, controllerDir));
6928
- writeTemplate(projectDir, `${controllerDir}/HealthController.java`, `package ${appPackage}.controller;
7032
+ writeTemplate(
7033
+ projectDir,
7034
+ `${controllerDir}/HealthController.java`,
7035
+ `package ${appPackage}.controller;
6929
7036
 
6930
7037
  import org.springframework.web.bind.annotation.GetMapping;
6931
7038
  import org.springframework.web.bind.annotation.RestController;
@@ -6938,7 +7045,8 @@ public class HealthController {
6938
7045
  return "OK";
6939
7046
  }
6940
7047
  }
6941
- `);
7048
+ `
7049
+ );
6942
7050
  const serviceDir = `${appDir}/service`;
6943
7051
  ensureDir2(path2.join(projectDir, serviceDir));
6944
7052
  if (hasOrm) {
@@ -6946,7 +7054,10 @@ public class HealthController {
6946
7054
  ensureDir2(path2.join(projectDir, repositoryDir));
6947
7055
  const entityDir = `${appDir}/entity`;
6948
7056
  ensureDir2(path2.join(projectDir, entityDir));
6949
- writeTemplate(projectDir, `${entityDir}/BaseEntity.java`, `package ${appPackage}.entity;
7057
+ writeTemplate(
7058
+ projectDir,
7059
+ `${entityDir}/BaseEntity.java`,
7060
+ `package ${appPackage}.entity;
6950
7061
 
6951
7062
  import jakarta.persistence.GeneratedValue;
6952
7063
  import jakarta.persistence.GenerationType;
@@ -6971,7 +7082,8 @@ public abstract class BaseEntity {
6971
7082
  public LocalDateTime getUpdatedAt() { return updatedAt; }
6972
7083
  public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
6973
7084
  }
6974
- `);
7085
+ `
7086
+ );
6975
7087
  }
6976
7088
  ensureDir2(path2.join(projectDir, "src/main/resources"));
6977
7089
  const datasourceConfig = hasOrm ? `
@@ -6993,19 +7105,26 @@ spring:
6993
7105
  liquibase:
6994
7106
  change-log: classpath:db/changelog/db.changelog-master.xml
6995
7107
  ` : "";
6996
- writeTemplate(projectDir, "src/main/resources/application.yml", `server:
7108
+ writeTemplate(
7109
+ projectDir,
7110
+ "src/main/resources/application.yml",
7111
+ `server:
6997
7112
  port: 8080
6998
7113
  ${datasourceConfig}${liquibaseConfig}
6999
7114
  logging:
7000
7115
  level:
7001
7116
  root: INFO
7002
7117
  ${ctx.packageName}: DEBUG
7003
- `);
7118
+ `
7119
+ );
7004
7120
  if (hasLiquibase) {
7005
7121
  const now = /* @__PURE__ */ new Date();
7006
7122
  const ts = now.toISOString().replace(/\D/g, "").slice(0, 12);
7007
7123
  ensureDir2(path2.join(projectDir, "src/main/resources/db/changelog"));
7008
- writeTemplate(projectDir, "src/main/resources/db/changelog/db.changelog-master.xml", `<?xml version="1.0" encoding="UTF-8"?>
7124
+ writeTemplate(
7125
+ projectDir,
7126
+ "src/main/resources/db/changelog/db.changelog-master.xml",
7127
+ `<?xml version="1.0" encoding="UTF-8"?>
7009
7128
  <databaseChangeLog
7010
7129
  xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
7011
7130
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -7018,12 +7137,16 @@ logging:
7018
7137
  </createTable>
7019
7138
  </changeSet>
7020
7139
  </databaseChangeLog>
7021
- `);
7140
+ `
7141
+ );
7022
7142
  }
7023
7143
  const testDir = `src/test/java/${appPackage.replace(/\./g, "/")}`;
7024
7144
  ensureDir2(path2.join(projectDir, testDir));
7025
7145
  ensureDir2(path2.join(projectDir, "src/test/resources"));
7026
- writeTemplate(projectDir, `${testDir}/ApplicationTests.java`, `package ${appPackage};
7146
+ writeTemplate(
7147
+ projectDir,
7148
+ `${testDir}/ApplicationTests.java`,
7149
+ `package ${appPackage};
7027
7150
 
7028
7151
  import org.junit.jupiter.api.Test;
7029
7152
  import org.springframework.boot.test.context.SpringBootTest;
@@ -7035,19 +7158,31 @@ class ApplicationTests {
7035
7158
  void contextLoads() {
7036
7159
  }
7037
7160
  }
7038
- `);
7039
- writeTemplate(projectDir, "src/test/resources/junit-platform.properties", `junit.jupiter.testinstance.lifecycle.default = per_class
7040
- `);
7161
+ `
7162
+ );
7163
+ writeTemplate(
7164
+ projectDir,
7165
+ "src/test/resources/junit-platform.properties",
7166
+ `junit.jupiter.testinstance.lifecycle.default = per_class
7167
+ `
7168
+ );
7041
7169
  ensureDir2(path2.join(projectDir, "gradle/wrapper"));
7042
- writeTemplate(projectDir, "gradle/wrapper/gradle-wrapper.properties", `distributionBase=GRADLE_USER_HOME
7170
+ writeTemplate(
7171
+ projectDir,
7172
+ "gradle/wrapper/gradle-wrapper.properties",
7173
+ `distributionBase=GRADLE_USER_HOME
7043
7174
  distributionPath=wrapper/dists
7044
7175
  distributionUrl=https\\://services.gradle.org/distributions/gradle-8.12-bin.zip
7045
7176
  networkTimeout=10000
7046
7177
  validateDistributionUrl=true
7047
7178
  zipStoreBase=GRADLE_USER_HOME
7048
7179
  zipStorePath=wrapper/dists
7049
- `);
7050
- writeTemplate(projectDir, "gradlew", `#!/bin/sh
7180
+ `
7181
+ );
7182
+ writeTemplate(
7183
+ projectDir,
7184
+ "gradlew",
7185
+ `#!/bin/sh
7051
7186
 
7052
7187
  #
7053
7188
  # Gradle start up script for POSIX generated by Gradle.
@@ -7105,8 +7240,12 @@ else
7105
7240
  fi
7106
7241
 
7107
7242
  exec "$JAVACMD" $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
7108
- `);
7109
- writeTemplate(projectDir, "gradlew.bat", `@rem
7243
+ `
7244
+ );
7245
+ writeTemplate(
7246
+ projectDir,
7247
+ "gradlew.bat",
7248
+ `@rem
7110
7249
  @rem Gradle startup script for Windows
7111
7250
  @rem
7112
7251
  @if "%DEBUG%"=="" @echo off
@@ -7119,7 +7258,8 @@ set CLASSPATH=%APP_HOME%/gradle/wrapper/gradle-wrapper.jar
7119
7258
  "%JAVA_HOME%/bin/java.exe" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
7120
7259
  :end
7121
7260
  @endlocal & exit /b %ERRORLEVEL%
7122
- `);
7261
+ `
7262
+ );
7123
7263
  }
7124
7264
  function renderNestJSTemplate(projectDir, ctx, opts) {
7125
7265
  const hasPrisma = opts.nodejsOrm === "prisma";
@@ -7163,127 +7303,159 @@ function renderNestJSTemplate(projectDir, ctx, opts) {
7163
7303
  if (hasAgentSdk) {
7164
7304
  pkgDeps["@anthropic-ai/sdk"] = "^0.40.0";
7165
7305
  }
7166
- writeTemplate(projectDir, "package.json", JSON.stringify({
7167
- name: ctx.packageName,
7168
- version: "0.1.0",
7169
- description: "",
7170
- private: true,
7171
- scripts: {
7172
- build: "nest build",
7173
- format: 'prettier --write "src/**/*.ts"',
7174
- start: "nest start",
7175
- "start:dev": "nest start --watch",
7176
- "start:prod": "node dist/main",
7177
- lint: 'eslint "{src,test}/**/*.ts" --fix',
7178
- test: "jest",
7179
- "test:watch": "jest --watch",
7180
- "test:cov": "jest --coverage",
7181
- "test:e2e": "jest --config ./test/jest-e2e.json"
7182
- },
7183
- dependencies: pkgDeps,
7184
- devDependencies: devDeps,
7185
- jest: {
7186
- moduleFileExtensions: ["js", "json", "ts"],
7187
- rootDir: "src",
7188
- testRegex: ".*\\.spec\\.ts$",
7189
- transform: { "^.+\\.(t|j)s$": "ts-jest" },
7190
- collectCoverageFrom: ["**/*.(t|j)s"],
7191
- coverageDirectory: "../coverage",
7192
- testEnvironment: "node"
7193
- }
7194
- }, null, 2));
7195
- writeTemplate(projectDir, "tsconfig.json", JSON.stringify({
7196
- compilerOptions: {
7197
- module: "commonjs",
7198
- declaration: true,
7199
- removeComments: true,
7200
- emitDecoratorMetadata: true,
7201
- experimentalDecorators: true,
7202
- allowSyntheticDefaultImports: true,
7203
- target: "ES2023",
7204
- sourceMap: true,
7205
- outDir: "./dist",
7206
- baseUrl: "./",
7207
- incremental: true,
7208
- skipLibCheck: true,
7209
- strict: true,
7210
- strictNullChecks: true,
7211
- noImplicitAny: true,
7212
- strictBindCallApply: true,
7213
- forceConsistentCasingInFileNames: true,
7214
- noFallthroughCasesInSwitch: true,
7215
- esModuleInterop: true,
7216
- resolveJsonModule: true
7217
- }
7218
- }, null, 2));
7219
- writeTemplate(projectDir, "nest-cli.json", JSON.stringify({
7220
- $schema: "https://json.schemastore.org/nest-cli",
7221
- collection: "@nestjs/schematics",
7222
- sourceRoot: "src",
7223
- compilerOptions: { deleteOutDir: true }
7224
- }, null, 2));
7225
- writeTemplate(projectDir, ".prettierrc", JSON.stringify({
7226
- singleQuote: true,
7227
- trailingComma: "all",
7228
- tabWidth: 2,
7229
- semi: true,
7230
- printWidth: 120
7231
- }, null, 2));
7306
+ writeTemplate(
7307
+ projectDir,
7308
+ "package.json",
7309
+ JSON.stringify(
7310
+ {
7311
+ name: ctx.packageName,
7312
+ version: "0.1.0",
7313
+ description: "",
7314
+ private: true,
7315
+ scripts: {
7316
+ build: "nest build",
7317
+ format: 'prettier --write "src/**/*.ts"',
7318
+ start: "nest start",
7319
+ "start:dev": "nest start --watch",
7320
+ "start:prod": "node dist/main",
7321
+ lint: 'eslint "{src,test}/**/*.ts" --fix',
7322
+ test: "jest",
7323
+ "test:watch": "jest --watch",
7324
+ "test:cov": "jest --coverage",
7325
+ "test:e2e": "jest --config ./test/jest-e2e.json"
7326
+ },
7327
+ dependencies: pkgDeps,
7328
+ devDependencies: devDeps,
7329
+ jest: {
7330
+ moduleFileExtensions: ["js", "json", "ts"],
7331
+ rootDir: "src",
7332
+ testRegex: ".*\\.spec\\.ts$",
7333
+ transform: { "^.+\\.(t|j)s$": "ts-jest" },
7334
+ collectCoverageFrom: ["**/*.(t|j)s"],
7335
+ coverageDirectory: "../coverage",
7336
+ testEnvironment: "node"
7337
+ }
7338
+ },
7339
+ null,
7340
+ 2
7341
+ )
7342
+ );
7343
+ writeTemplate(
7344
+ projectDir,
7345
+ "tsconfig.json",
7346
+ JSON.stringify(
7347
+ {
7348
+ compilerOptions: {
7349
+ module: "commonjs",
7350
+ declaration: true,
7351
+ removeComments: true,
7352
+ emitDecoratorMetadata: true,
7353
+ experimentalDecorators: true,
7354
+ allowSyntheticDefaultImports: true,
7355
+ target: "ES2023",
7356
+ sourceMap: true,
7357
+ outDir: "./dist",
7358
+ baseUrl: "./",
7359
+ incremental: true,
7360
+ skipLibCheck: true,
7361
+ strict: true,
7362
+ strictNullChecks: true,
7363
+ noImplicitAny: true,
7364
+ strictBindCallApply: true,
7365
+ forceConsistentCasingInFileNames: true,
7366
+ noFallthroughCasesInSwitch: true,
7367
+ esModuleInterop: true,
7368
+ resolveJsonModule: true
7369
+ }
7370
+ },
7371
+ null,
7372
+ 2
7373
+ )
7374
+ );
7375
+ writeTemplate(
7376
+ projectDir,
7377
+ "nest-cli.json",
7378
+ JSON.stringify(
7379
+ {
7380
+ $schema: "https://json.schemastore.org/nest-cli",
7381
+ collection: "@nestjs/schematics",
7382
+ sourceRoot: "src",
7383
+ compilerOptions: { deleteOutDir: true }
7384
+ },
7385
+ null,
7386
+ 2
7387
+ )
7388
+ );
7389
+ writeTemplate(
7390
+ projectDir,
7391
+ ".prettierrc",
7392
+ JSON.stringify(
7393
+ {
7394
+ singleQuote: true,
7395
+ trailingComma: "all",
7396
+ tabWidth: 2,
7397
+ semi: true,
7398
+ printWidth: 120
7399
+ },
7400
+ null,
7401
+ 2
7402
+ )
7403
+ );
7232
7404
  ensureDir2(path2.join(projectDir, "src"));
7233
- writeTemplate(projectDir, "src/main.ts", [
7234
- "import { NestFactory } from '@nestjs/core';",
7235
- "import { ValidationPipe, Logger } from '@nestjs/common';",
7236
- "import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';",
7237
- "import { AppModule } from './app.module';",
7238
- "",
7239
- "async function bootstrap() {",
7240
- " const app = await NestFactory.create(AppModule);",
7241
- " const logger = new Logger('Bootstrap');",
7242
- "",
7243
- " app.setGlobalPrefix('api/v1');",
7244
- "",
7245
- " app.useGlobalPipes(",
7246
- " new ValidationPipe({",
7247
- " whitelist: true,",
7248
- " forbidNonWhitelisted: true,",
7249
- " transform: true,",
7250
- " }),",
7251
- " );",
7252
- "",
7253
- " const config = new DocumentBuilder()",
7254
- ` .setTitle('${ctx.projectName}')`,
7255
- " .setDescription('API documentation')",
7256
- " .setVersion('1.0')",
7257
- " .addBearerAuth()",
7258
- " .build();",
7259
- " const document = SwaggerModule.createDocument(app, config);",
7260
- " SwaggerModule.setup('api/docs', app, document);",
7261
- "",
7262
- " app.enableCors({",
7263
- " origin: process.env.CORS_ORIGIN || '*',",
7264
- " methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',",
7265
- " credentials: true,",
7266
- " });",
7267
- "",
7268
- " const port = process.env.PORT || 3000;",
7269
- " await app.listen(port);",
7270
- " logger.log(`Application running on http://localhost:${port}`);",
7271
- " logger.log(`Swagger docs at http://localhost:${port}/api/docs`);",
7272
- "}",
7273
- "bootstrap();",
7274
- ""
7275
- ].join("\n"));
7405
+ writeTemplate(
7406
+ projectDir,
7407
+ "src/main.ts",
7408
+ [
7409
+ "import { NestFactory } from '@nestjs/core';",
7410
+ "import { ValidationPipe, Logger } from '@nestjs/common';",
7411
+ "import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';",
7412
+ "import { AppModule } from './app.module';",
7413
+ "",
7414
+ "async function bootstrap() {",
7415
+ " const app = await NestFactory.create(AppModule);",
7416
+ " const logger = new Logger('Bootstrap');",
7417
+ "",
7418
+ " app.setGlobalPrefix('api/v1');",
7419
+ "",
7420
+ " app.useGlobalPipes(",
7421
+ " new ValidationPipe({",
7422
+ " whitelist: true,",
7423
+ " forbidNonWhitelisted: true,",
7424
+ " transform: true,",
7425
+ " }),",
7426
+ " );",
7427
+ "",
7428
+ " const config = new DocumentBuilder()",
7429
+ ` .setTitle('${ctx.projectName}')`,
7430
+ " .setDescription('API documentation')",
7431
+ " .setVersion('1.0')",
7432
+ " .addBearerAuth()",
7433
+ " .build();",
7434
+ " const document = SwaggerModule.createDocument(app, config);",
7435
+ " SwaggerModule.setup('api/docs', app, document);",
7436
+ "",
7437
+ " app.enableCors({",
7438
+ " origin: process.env.CORS_ORIGIN || '*',",
7439
+ " methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',",
7440
+ " credentials: true,",
7441
+ " });",
7442
+ "",
7443
+ " const port = process.env.PORT || 3000;",
7444
+ " await app.listen(port);",
7445
+ " logger.log(`Application running on http://localhost:${port}`);",
7446
+ " logger.log(`Swagger docs at http://localhost:${port}/api/docs`);",
7447
+ "}",
7448
+ "bootstrap();",
7449
+ ""
7450
+ ].join("\n")
7451
+ );
7276
7452
  const appModuleContent = [
7277
7453
  "import { Module } from '@nestjs/common';",
7278
7454
  "import { ConfigModule } from '@nestjs/config';",
7279
7455
  "import { AppController } from './app.controller';",
7280
7456
  "import { AppService } from './app.service';"
7281
7457
  ];
7282
- const appModuleImports = [
7283
- " ConfigModule.forRoot({",
7284
- " isGlobal: true,",
7285
- " }),"
7286
- ];
7458
+ const appModuleImports = [" ConfigModule.forRoot({", " isGlobal: true,", " }),"];
7287
7459
  if (hasPrisma) {
7288
7460
  appModuleContent.push("import { PrismaModule } from './prisma/prisma.module';");
7289
7461
  appModuleImports.push(" PrismaModule,");
@@ -7305,188 +7477,223 @@ function renderNestJSTemplate(projectDir, ctx, opts) {
7305
7477
  appModuleContent.push("})");
7306
7478
  appModuleContent.push("export class AppModule {}");
7307
7479
  writeTemplate(projectDir, "src/app.module.ts", appModuleContent.join("\n"));
7308
- writeTemplate(projectDir, "src/app.controller.ts", [
7309
- "import { Controller, Get } from '@nestjs/common';",
7310
- "import { ApiTags, ApiOperation } from '@nestjs/swagger';",
7311
- "import { AppService } from './app.service';",
7312
- "",
7313
- "@ApiTags('Health')",
7314
- "@Controller()",
7315
- "export class AppController {",
7316
- " constructor(private readonly appService: AppService) {}",
7317
- "",
7318
- " @Get()",
7319
- " @ApiOperation({ summary: '\u5065\u5EB7\u68C0\u67E5' })",
7320
- " getHello(): string {",
7321
- " return this.appService.getHello();",
7322
- " }",
7323
- "}",
7324
- ""
7325
- ].join("\n"));
7326
- writeTemplate(projectDir, "src/app.service.ts", [
7327
- "import { Injectable } from '@nestjs/common';",
7328
- "",
7329
- "@Injectable()",
7330
- "export class AppService {",
7331
- " getHello(): string {",
7332
- ` return 'Hello from ${ctx.projectName}!';`,
7333
- " }",
7334
- "}",
7335
- ""
7336
- ].join("\n"));
7337
- if (hasPrisma) {
7338
- ensureDir2(path2.join(projectDir, "prisma"));
7339
- writeTemplate(projectDir, "prisma/schema.prisma", [
7340
- "generator client {",
7341
- ' provider = "prisma-client-js"',
7342
- "}",
7480
+ writeTemplate(
7481
+ projectDir,
7482
+ "src/app.controller.ts",
7483
+ [
7484
+ "import { Controller, Get } from '@nestjs/common';",
7485
+ "import { ApiTags, ApiOperation } from '@nestjs/swagger';",
7486
+ "import { AppService } from './app.service';",
7343
7487
  "",
7344
- "datasource db {",
7345
- ' provider = "postgresql"',
7346
- ' url = env("DATABASE_URL")',
7347
- "}",
7488
+ "@ApiTags('Health')",
7489
+ "@Controller()",
7490
+ "export class AppController {",
7491
+ " constructor(private readonly appService: AppService) {}",
7348
7492
  "",
7349
- "/// \u7528\u6237",
7350
- "model User {",
7351
- " id Int @id @default(autoincrement())",
7352
- " name String",
7353
- " email String @unique",
7354
- " active Boolean @default(true)",
7355
- " createdAt DateTime @default(now())",
7356
- " updatedAt DateTime @updatedAt",
7357
- "",
7358
- ' @@map("users")',
7493
+ " @Get()",
7494
+ " @ApiOperation({ summary: '\u5065\u5EB7\u68C0\u67E5' })",
7495
+ " getHello(): string {",
7496
+ " return this.appService.getHello();",
7497
+ " }",
7359
7498
  "}",
7360
7499
  ""
7361
- ].join("\n"));
7362
- ensureDir2(path2.join(projectDir, "src/prisma"));
7363
- writeTemplate(projectDir, "src/prisma/prisma.module.ts", [
7364
- "import { Global, Module } from '@nestjs/common';",
7365
- "import { PrismaService } from './prisma.service';",
7366
- "",
7367
- "@Global()",
7368
- "@Module({",
7369
- " providers: [PrismaService],",
7370
- " exports: [PrismaService],",
7371
- "})",
7372
- "export class PrismaModule {}",
7373
- ""
7374
- ].join("\n"));
7375
- writeTemplate(projectDir, "src/prisma/prisma.service.ts", [
7376
- "import { Injectable, Logger, OnModuleInit, OnModuleDestroy } from '@nestjs/common';",
7377
- "import { PrismaClient } from '@prisma/client';",
7500
+ ].join("\n")
7501
+ );
7502
+ writeTemplate(
7503
+ projectDir,
7504
+ "src/app.service.ts",
7505
+ [
7506
+ "import { Injectable } from '@nestjs/common';",
7378
7507
  "",
7379
7508
  "@Injectable()",
7380
- "export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {",
7381
- " private readonly logger = new Logger(PrismaService.name);",
7382
- "",
7383
- " async onModuleInit() {",
7384
- " await this.$connect();",
7385
- " this.logger.log('Database connected');",
7386
- " }",
7387
- "",
7388
- " async onModuleDestroy() {",
7389
- " await this.$disconnect();",
7390
- " this.logger.log('Database disconnected');",
7509
+ "export class AppService {",
7510
+ " getHello(): string {",
7511
+ ` return 'Hello from ${ctx.projectName}!';`,
7391
7512
  " }",
7392
7513
  "}",
7393
7514
  ""
7394
- ].join("\n"));
7515
+ ].join("\n")
7516
+ );
7517
+ if (hasPrisma) {
7518
+ ensureDir2(path2.join(projectDir, "prisma"));
7519
+ writeTemplate(
7520
+ projectDir,
7521
+ "prisma/schema.prisma",
7522
+ [
7523
+ "generator client {",
7524
+ ' provider = "prisma-client-js"',
7525
+ "}",
7526
+ "",
7527
+ "datasource db {",
7528
+ ' provider = "postgresql"',
7529
+ ' url = env("DATABASE_URL")',
7530
+ "}",
7531
+ "",
7532
+ "/// \u7528\u6237",
7533
+ "model User {",
7534
+ " id Int @id @default(autoincrement())",
7535
+ " name String",
7536
+ " email String @unique",
7537
+ " active Boolean @default(true)",
7538
+ " createdAt DateTime @default(now())",
7539
+ " updatedAt DateTime @updatedAt",
7540
+ "",
7541
+ ' @@map("users")',
7542
+ "}",
7543
+ ""
7544
+ ].join("\n")
7545
+ );
7546
+ ensureDir2(path2.join(projectDir, "src/prisma"));
7547
+ writeTemplate(
7548
+ projectDir,
7549
+ "src/prisma/prisma.module.ts",
7550
+ [
7551
+ "import { Global, Module } from '@nestjs/common';",
7552
+ "import { PrismaService } from './prisma.service';",
7553
+ "",
7554
+ "@Global()",
7555
+ "@Module({",
7556
+ " providers: [PrismaService],",
7557
+ " exports: [PrismaService],",
7558
+ "})",
7559
+ "export class PrismaModule {}",
7560
+ ""
7561
+ ].join("\n")
7562
+ );
7563
+ writeTemplate(
7564
+ projectDir,
7565
+ "src/prisma/prisma.service.ts",
7566
+ [
7567
+ "import { Injectable, Logger, OnModuleInit, OnModuleDestroy } from '@nestjs/common';",
7568
+ "import { PrismaClient } from '@prisma/client';",
7569
+ "",
7570
+ "@Injectable()",
7571
+ "export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {",
7572
+ " private readonly logger = new Logger(PrismaService.name);",
7573
+ "",
7574
+ " async onModuleInit() {",
7575
+ " await this.$connect();",
7576
+ " this.logger.log('Database connected');",
7577
+ " }",
7578
+ "",
7579
+ " async onModuleDestroy() {",
7580
+ " await this.$disconnect();",
7581
+ " this.logger.log('Database disconnected');",
7582
+ " }",
7583
+ "}",
7584
+ ""
7585
+ ].join("\n")
7586
+ );
7395
7587
  }
7396
7588
  if (hasAgentSdk) {
7397
7589
  ensureDir2(path2.join(projectDir, "src/agent/tools"));
7398
- writeTemplate(projectDir, "src/agent/agent.module.ts", [
7399
- "import { Module } from '@nestjs/common';",
7400
- "import { AgentService } from './agent.service';",
7401
- "",
7402
- "@Module({",
7403
- " providers: [AgentService],",
7404
- " exports: [AgentService],",
7405
- "})",
7406
- "export class AgentModule {}",
7407
- ""
7408
- ].join("\n"));
7409
- writeTemplate(projectDir, "src/agent/agent.service.ts", [
7410
- "import { Injectable, Logger } from '@nestjs/common';",
7411
- "import { ConfigService } from '@nestjs/config';",
7412
- "",
7413
- "/**",
7414
- " * Claude Agent \u670D\u52A1",
7415
- " *",
7416
- " * \u5C01\u88C5 Claude Agent SDK\uFF0C\u63D0\u4F9B Agent \u4EA4\u4E92\u80FD\u529B\u3002",
7417
- " * \u4F7F\u7528\u524D\u9700\u5728 .env \u4E2D\u914D\u7F6E ANTHROPIC_API_KEY\u3002",
7418
- " */",
7419
- "@Injectable()",
7420
- "export class AgentService {",
7421
- " private readonly logger = new Logger(AgentService.name);",
7422
- "",
7423
- " constructor(private readonly configService: ConfigService) {",
7424
- " const apiKey = this.configService.get<string>('ANTHROPIC_API_KEY');",
7425
- " if (!apiKey) {",
7426
- " this.logger.warn('ANTHROPIC_API_KEY is not configured');",
7427
- " }",
7428
- " }",
7429
- "",
7430
- " async ask(question: string): Promise<string> {",
7431
- ' this.logger.debug(`Agent ask: "${question}"`);',
7432
- " return 'Agent response placeholder -- configure ANTHROPIC_API_KEY to enable';",
7433
- " }",
7434
- "}",
7435
- ""
7436
- ].join("\n"));
7437
- writeTemplate(projectDir, "src/agent/tools/example.tool.ts", [
7438
- "import { Injectable, Logger } from '@nestjs/common';",
7439
- "",
7440
- "export interface ToolDefinition {",
7441
- " name: string;",
7442
- " description: string;",
7443
- " input_schema: Record<string, unknown>;",
7444
- " execute: (input: Record<string, unknown>) => Promise<unknown>;",
7445
- "}",
7446
- "",
7447
- "@Injectable()",
7448
- "export class ExampleTool implements ToolDefinition {",
7449
- " private readonly logger = new Logger(ExampleTool.name);",
7450
- "",
7451
- " name = 'get_current_time';",
7452
- " description = '\u83B7\u53D6\u5F53\u524D\u65E5\u671F\u548C\u65F6\u95F4';",
7453
- " input_schema = {",
7454
- " type: 'object',",
7455
- " properties: {",
7456
- " timezone: {",
7457
- " type: 'string',",
7458
- " description: '\u65F6\u533A\uFF08\u53EF\u9009\uFF0C\u9ED8\u8BA4 UTC\uFF09',",
7459
- " enum: ['UTC', 'Asia/Shanghai', 'America/New_York'],",
7460
- " },",
7461
- " },",
7462
- " };",
7463
- "",
7464
- " async execute(input: Record<string, unknown>): Promise<unknown> {",
7465
- " try {",
7466
- " this.logger.debug(`Tool execute: get_current_time with input: ${JSON.stringify(input)}`);",
7467
- " const now = new Date();",
7468
- " return {",
7469
- " success: true,",
7470
- " data: {",
7471
- " iso: now.toISOString(),",
7472
- " timestamp: now.getTime(),",
7473
- " },",
7474
- " };",
7475
- " } catch (error) {",
7476
- " this.logger.error(`Tool execution failed: ${error}`);",
7477
- " return {",
7478
- " success: false,",
7479
- " error: error instanceof Error ? error.message : 'Unknown error',",
7480
- " };",
7481
- " }",
7482
- " }",
7483
- "}",
7484
- ""
7485
- ].join("\n"));
7590
+ writeTemplate(
7591
+ projectDir,
7592
+ "src/agent/agent.module.ts",
7593
+ [
7594
+ "import { Module } from '@nestjs/common';",
7595
+ "import { AgentService } from './agent.service';",
7596
+ "",
7597
+ "@Module({",
7598
+ " providers: [AgentService],",
7599
+ " exports: [AgentService],",
7600
+ "})",
7601
+ "export class AgentModule {}",
7602
+ ""
7603
+ ].join("\n")
7604
+ );
7605
+ writeTemplate(
7606
+ projectDir,
7607
+ "src/agent/agent.service.ts",
7608
+ [
7609
+ "import { Injectable, Logger } from '@nestjs/common';",
7610
+ "import { ConfigService } from '@nestjs/config';",
7611
+ "",
7612
+ "/**",
7613
+ " * Claude Agent \u670D\u52A1",
7614
+ " *",
7615
+ " * \u5C01\u88C5 Claude Agent SDK\uFF0C\u63D0\u4F9B Agent \u4EA4\u4E92\u80FD\u529B\u3002",
7616
+ " * \u4F7F\u7528\u524D\u9700\u5728 .env \u4E2D\u914D\u7F6E ANTHROPIC_API_KEY\u3002",
7617
+ " */",
7618
+ "@Injectable()",
7619
+ "export class AgentService {",
7620
+ " private readonly logger = new Logger(AgentService.name);",
7621
+ "",
7622
+ " constructor(private readonly configService: ConfigService) {",
7623
+ " const apiKey = this.configService.get<string>('ANTHROPIC_API_KEY');",
7624
+ " if (!apiKey) {",
7625
+ " this.logger.warn('ANTHROPIC_API_KEY is not configured');",
7626
+ " }",
7627
+ " }",
7628
+ "",
7629
+ " async ask(question: string): Promise<string> {",
7630
+ ' this.logger.debug(`Agent ask: "${question}"`);',
7631
+ " return 'Agent response placeholder -- configure ANTHROPIC_API_KEY to enable';",
7632
+ " }",
7633
+ "}",
7634
+ ""
7635
+ ].join("\n")
7636
+ );
7637
+ writeTemplate(
7638
+ projectDir,
7639
+ "src/agent/tools/example.tool.ts",
7640
+ [
7641
+ "import { Injectable, Logger } from '@nestjs/common';",
7642
+ "",
7643
+ "export interface ToolDefinition {",
7644
+ " name: string;",
7645
+ " description: string;",
7646
+ " input_schema: Record<string, unknown>;",
7647
+ " execute: (input: Record<string, unknown>) => Promise<unknown>;",
7648
+ "}",
7649
+ "",
7650
+ "@Injectable()",
7651
+ "export class ExampleTool implements ToolDefinition {",
7652
+ " private readonly logger = new Logger(ExampleTool.name);",
7653
+ "",
7654
+ " name = 'get_current_time';",
7655
+ " description = '\u83B7\u53D6\u5F53\u524D\u65E5\u671F\u548C\u65F6\u95F4';",
7656
+ " input_schema = {",
7657
+ " type: 'object',",
7658
+ " properties: {",
7659
+ " timezone: {",
7660
+ " type: 'string',",
7661
+ " description: '\u65F6\u533A\uFF08\u53EF\u9009\uFF0C\u9ED8\u8BA4 UTC\uFF09',",
7662
+ " enum: ['UTC', 'Asia/Shanghai', 'America/New_York'],",
7663
+ " },",
7664
+ " },",
7665
+ " };",
7666
+ "",
7667
+ " async execute(input: Record<string, unknown>): Promise<unknown> {",
7668
+ " try {",
7669
+ " this.logger.debug(`Tool execute: get_current_time with input: ${JSON.stringify(input)}`);",
7670
+ " const now = new Date();",
7671
+ " return {",
7672
+ " success: true,",
7673
+ " data: {",
7674
+ " iso: now.toISOString(),",
7675
+ " timestamp: now.getTime(),",
7676
+ " },",
7677
+ " };",
7678
+ " } catch (error) {",
7679
+ " this.logger.error(`Tool execution failed: ${error}`);",
7680
+ " return {",
7681
+ " success: false,",
7682
+ " error: error instanceof Error ? error.message : 'Unknown error',",
7683
+ " };",
7684
+ " }",
7685
+ " }",
7686
+ "}",
7687
+ ""
7688
+ ].join("\n")
7689
+ );
7486
7690
  }
7487
7691
  }
7488
7692
  function renderCommonFiles(projectDir, ctx, opts) {
7489
- writeTemplate(projectDir, ".gitignore", `node_modules/
7693
+ writeTemplate(
7694
+ projectDir,
7695
+ ".gitignore",
7696
+ `node_modules/
7490
7697
  .gradle/
7491
7698
  build/
7492
7699
  target/
@@ -7499,10 +7706,13 @@ __pycache__/
7499
7706
  .idea/
7500
7707
  .vscode/
7501
7708
  *.iml
7502
- `);
7709
+ `
7710
+ );
7503
7711
  const hasNodeBackend = opts?.backend === "nodejs";
7504
7712
  if (hasNodeBackend) {
7505
- writeEnvFile(projectDir, `# \u6570\u636E\u5E93\u914D\u7F6E
7713
+ writeEnvFile(
7714
+ projectDir,
7715
+ `# \u6570\u636E\u5E93\u914D\u7F6E
7506
7716
  DATABASE_URL=postgresql://user:password@localhost:5432/${ctx.packageName}
7507
7717
 
7508
7718
  # Claude Agent SDK
@@ -7512,16 +7722,20 @@ AGENT_MODEL=claude-sonnet-5
7512
7722
  # Server
7513
7723
  PORT=3000
7514
7724
  CORS_ORIGIN=http://localhost:4200
7515
- `);
7725
+ `
7726
+ );
7516
7727
  } else {
7517
- writeEnvFile(projectDir, `# \u6570\u636E\u5E93\u914D\u7F6E
7728
+ writeEnvFile(
7729
+ projectDir,
7730
+ `# \u6570\u636E\u5E93\u914D\u7F6E
7518
7731
  DB_URL=jdbc:postgresql://localhost:5432/${ctx.packageName}
7519
7732
  DB_USERNAME=postgres
7520
7733
  DB_PASSWORD=change-me
7521
7734
 
7522
7735
  # API Keys
7523
7736
  OPENAI_API_KEY=sk-your-key-here
7524
- `);
7737
+ `
7738
+ );
7525
7739
  }
7526
7740
  const readme = generateReadme(ctx, opts);
7527
7741
  writeTemplate(projectDir, "README.md", readme);
@@ -7743,22 +7957,22 @@ function installGrillMe(targetDir) {
7743
7957
  const skillDir = path3.join(targetDir, ".claude", "skills", "grill-me");
7744
7958
  fs3.mkdirSync(skillDir, { recursive: true });
7745
7959
  try {
7746
- (0, import_node_child_process.execSync)(
7747
- "curl -fsSL https://raw.githubusercontent.com/mattpocock/skills/main/grill-me/SKILL.md -o SKILL.md",
7748
- { ...EXEC_OPTIONS, cwd: skillDir }
7749
- );
7960
+ (0, import_node_child_process.execSync)("curl -fsSL https://raw.githubusercontent.com/mattpocock/skills/main/grill-me/SKILL.md -o SKILL.md", {
7961
+ ...EXEC_OPTIONS,
7962
+ cwd: skillDir
7963
+ });
7750
7964
  console.log("\u2714 grill-me skill \u5DF2\u5B89\u88C5");
7751
7965
  } catch {
7752
7966
  console.log("\u26A0 grill-me \u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u5B89\u88C5 https://github.com/mattpocock/skills");
7753
7967
  }
7754
7968
  }
7755
- function isPlaywrightInstalled(targetDir) {
7969
+ function isPlaywrightInstalled() {
7756
7970
  const result = execSafe("npx playwright install --dry-run");
7757
7971
  return result.success && !result.output.toLowerCase().includes("to install");
7758
7972
  }
7759
- function installPlaywright(config, targetDir) {
7973
+ function installPlaywright(config, _targetDir) {
7760
7974
  if (config["sweep.e2eFramework"] !== "playwright") return;
7761
- if (isPlaywrightInstalled(targetDir)) {
7975
+ if (isPlaywrightInstalled()) {
7762
7976
  console.log("\u2139 Playwright \u6D4F\u89C8\u5668\u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u5B89\u88C5");
7763
7977
  return;
7764
7978
  }
@@ -7867,8 +8081,8 @@ function filterExistingKeys(content, existingKeys) {
7867
8081
  const lines = content.split("\n");
7868
8082
  const result = [];
7869
8083
  let skipUntilDecor = false;
7870
- for (let i = 0; i < lines.length; i++) {
7871
- const trimmed = lines[i].trim();
8084
+ for (const line of lines) {
8085
+ const trimmed = line.trim();
7872
8086
  if (trimmed.includes("=") && !trimmed.startsWith("#")) {
7873
8087
  const key = trimmed.slice(0, trimmed.indexOf("=")).trim();
7874
8088
  if (existingKeys.has(key)) {
@@ -7877,20 +8091,20 @@ function filterExistingKeys(content, existingKeys) {
7877
8091
  }
7878
8092
  }
7879
8093
  if (skipUntilDecor) {
7880
- if (lines[i].match(/^#\s*[═━]/)) {
8094
+ if (line.match(/^#\s*[═━]/)) {
7881
8095
  skipUntilDecor = false;
7882
- result.push(lines[i]);
8096
+ result.push(line);
7883
8097
  }
7884
8098
  continue;
7885
8099
  }
7886
- result.push(lines[i]);
8100
+ result.push(line);
7887
8101
  }
7888
8102
  return result.join("\n").trimEnd();
7889
8103
  }
7890
8104
 
7891
8105
  // src/wizard/wizard-flow.ts
7892
- var p4 = __toESM(require("@clack/prompts"));
7893
8106
  var fs8 = __toESM(require("node:fs"));
8107
+ var p4 = __toESM(require("@clack/prompts"));
7894
8108
 
7895
8109
  // src/wizard/tool-select.ts
7896
8110
  var p = __toESM(require("@clack/prompts"));
@@ -8123,9 +8337,7 @@ async function renderAsSingleGroup(questions, multiselectKey, selectedTechs, con
8123
8337
  const hasOptionDependsOn = opts.some((o) => o.dependsOn);
8124
8338
  if (hasOptionDependsOn) {
8125
8339
  groupPrompts[sub.key] = ({ results }) => {
8126
- const filtered = opts.filter(
8127
- (o) => !o.dependsOn || Object.values(results).includes(o.dependsOn)
8128
- );
8340
+ const filtered = opts.filter((o) => !o.dependsOn || Object.values(results).includes(o.dependsOn));
8129
8341
  return p3.select({
8130
8342
  message: sub.label,
8131
8343
  options: filtered.map((o) => ({
@@ -8220,13 +8432,7 @@ async function runToolWizard(wizard, configuredKeys) {
8220
8432
  );
8221
8433
  if (hasTabs) {
8222
8434
  const selectedTechs = (config[question.key] || "").split(",").map((v) => v.trim()).filter(Boolean);
8223
- await renderAsSingleGroup(
8224
- wizard.questions,
8225
- question.key,
8226
- selectedTechs,
8227
- config,
8228
- configuredKeys
8229
- );
8435
+ await renderAsSingleGroup(wizard.questions, question.key, selectedTechs, config, configuredKeys);
8230
8436
  break;
8231
8437
  }
8232
8438
  } else if (question.dependsOn?.contains) {
@@ -8604,7 +8810,7 @@ function printMcpEnvStatus(mcpTools, installedMcpServices, examplesDir, targetDi
8604
8810
  }
8605
8811
  }
8606
8812
  async function printGuide(options) {
8607
- const { installedSkills, config, targetDir, mcpTools, mcpEnvStubs } = options;
8813
+ const { installedSkills, mcpTools, mcpEnvStubs } = options;
8608
8814
  console.log("");
8609
8815
  console.log(" \u2714 \u914D\u7F6E\u5DF2\u4FDD\u5B58\uFF08DeepStorm \u2192 .deepstorm/settings.json\uFF0CClaude Code \u2192 .claude/settings.json\uFF09");
8610
8816
  if (mcpTools && mcpTools.length > 0) {
@@ -8721,9 +8927,7 @@ function renderTemplate(tmplPath, variables, outputPath) {
8721
8927
  function copyVariants(sourceVariantsDir, selectedValue, targetDir, clean = false) {
8722
8928
  const variantDir = path8.join(sourceVariantsDir, selectedValue);
8723
8929
  if (!fs12.existsSync(variantDir)) {
8724
- console.warn(
8725
- `[template] \u26A0 \u53D8\u4F53\u76EE\u5F55\u4E0D\u5B58\u5728: ${variantDir}\uFF08\u76F8\u5BF9\u4E8E ${sourceVariantsDir}\uFF09\uFF0C\u8DF3\u8FC7`
8726
- );
8930
+ console.warn(`[template] \u26A0 \u53D8\u4F53\u76EE\u5F55\u4E0D\u5B58\u5728: ${variantDir}\uFF08\u76F8\u5BF9\u4E8E ${sourceVariantsDir}\uFF09\uFF0C\u8DF3\u8FC7`);
8727
8931
  return;
8728
8932
  }
8729
8933
  if (clean && fs12.existsSync(targetDir)) {
@@ -8758,10 +8962,10 @@ async function runSetup(reader, targetDir, cliDir2, options, registry2) {
8758
8962
  cleanInstalled(targetDir);
8759
8963
  console.log("\u2714 \u65E7\u5B89\u88C5\u5DF2\u6E05\u7406");
8760
8964
  }
8761
- let tools = [];
8762
- let config = {};
8965
+ let tools;
8966
+ let config;
8763
8967
  let selectedMcpTools = [];
8764
- let templateVariables = {};
8968
+ let templateVariables;
8765
8969
  let enabledMcpJsonServers = [];
8766
8970
  const examplesDir = path9.join(cliDir2, "env-examples");
8767
8971
  const installedSkillIds = [];
@@ -8799,7 +9003,16 @@ async function runSetup(reader, targetDir, cliDir2, options, registry2) {
8799
9003
  }
8800
9004
  installMcpSkills(tools, reader, cliDir2, targetDir, installedSkillIds, selectedMcpTools);
8801
9005
  for (const tool of tools) {
8802
- const ids = renderToolAssets(tool, reader, cliDir2, targetDir, templateVariables, config, registry2, selectedMcpTools);
9006
+ const ids = renderToolAssets(
9007
+ tool,
9008
+ reader,
9009
+ cliDir2,
9010
+ targetDir,
9011
+ templateVariables,
9012
+ config,
9013
+ registry2,
9014
+ selectedMcpTools
9015
+ );
8803
9016
  installedSkillIds.push(...ids);
8804
9017
  }
8805
9018
  if (shouldInstallGlobalHooks(tools, reader)) {
@@ -8832,8 +9045,8 @@ async function runSetup(reader, targetDir, cliDir2, options, registry2) {
8832
9045
  deepstormConfig.installedMcpServers = [.../* @__PURE__ */ new Set([...existingMcp, ...selectedMcpTools])];
8833
9046
  }
8834
9047
  writeDeepStormConfig(targetDir, deepstormConfig);
8835
- const frontend = nestedConfig.reef?.frontend?.framework ?? void 0;
8836
- const backend = nestedConfig.reef?.backend?.language ?? void 0;
9048
+ const frontend = deepstormConfig.reef?.frontend?.framework ?? void 0;
9049
+ const backend = deepstormConfig.reef?.backend?.language ?? void 0;
8837
9050
  initContextMap(targetDir, { frontend, backend, projectName: "" });
8838
9051
  const claudeSettingsPath = path9.join(targetDir, ".claude", "settings.json");
8839
9052
  if (enabledMcpJsonServers.length > 0) {
@@ -8924,11 +9137,7 @@ function renderToolAssets(tool, reader, cliDir2, targetDir, templateVariables, c
8924
9137
  registry2.mcpTools ?? {}
8925
9138
  );
8926
9139
  ensureDir3(targetDirPath);
8927
- renderTemplate(
8928
- tmplPath,
8929
- enrichedVars,
8930
- path9.join(targetDirPath, "SKILL.md")
8931
- );
9140
+ renderTemplate(tmplPath, enrichedVars, path9.join(targetDirPath, "SKILL.md"));
8932
9141
  copyVariantsForConfig(variantsDir, config, targetDirPath, skill.configKey);
8933
9142
  copyFragmentsForSkill(skillId, srcDir, config, registry2, targetDirPath);
8934
9143
  copyReferencesForSkill(srcDir, targetDirPath);
@@ -8953,11 +9162,7 @@ function renderToolAssets(tool, reader, cliDir2, targetDir, templateVariables, c
8953
9162
  const tmplSrcPath = path9.join(agentsSrcDir, agentFile.replace(/\.md$/, ".md.tmpl"));
8954
9163
  if (fs13.existsSync(tmplSrcPath)) {
8955
9164
  const outputName = agentFile.replace(/\.tmpl$/, "");
8956
- renderTemplate(
8957
- tmplSrcPath,
8958
- templateVariables,
8959
- path9.join(targetAgentsDir, outputName)
8960
- );
9165
+ renderTemplate(tmplSrcPath, templateVariables, path9.join(targetAgentsDir, outputName));
8961
9166
  }
8962
9167
  continue;
8963
9168
  }
@@ -8976,11 +9181,7 @@ function renderToolAssets(tool, reader, cliDir2, targetDir, templateVariables, c
8976
9181
  } else {
8977
9182
  const tmplPath = srcPath + ".tmpl";
8978
9183
  if (fs13.existsSync(tmplPath)) {
8979
- renderTemplate(
8980
- tmplPath,
8981
- templateVariables,
8982
- path9.join(targetHooksDir, hookFile)
8983
- );
9184
+ renderTemplate(tmplPath, templateVariables, path9.join(targetHooksDir, hookFile));
8984
9185
  }
8985
9186
  }
8986
9187
  }
@@ -9081,11 +9282,7 @@ function copyFragmentsForSkill(skillId, srcDir, config, registry2, targetDir) {
9081
9282
  for (const file of exampleFiles) {
9082
9283
  if (file === ".DS_Store") continue;
9083
9284
  ensureDir3(targetExamplesDir);
9084
- fs13.cpSync(
9085
- path9.join(srcExamples, file),
9086
- path9.join(targetExamplesDir, `${value}-${file}`),
9087
- { force: true }
9088
- );
9285
+ fs13.cpSync(path9.join(srcExamples, file), path9.join(targetExamplesDir, `${value}-${file}`), { force: true });
9089
9286
  }
9090
9287
  }
9091
9288
  const allFiles = fs13.readdirSync(srcPath);
@@ -9305,11 +9502,7 @@ async function buildPlugin(config) {
9305
9502
  }
9306
9503
  }
9307
9504
  if (Object.keys(mcpServers).length > 0) {
9308
- fs14.writeFileSync(
9309
- path10.join(outputDir, ".mcp.json"),
9310
- JSON.stringify({ mcpServers }, null, 2) + "\n",
9311
- "utf-8"
9312
- );
9505
+ fs14.writeFileSync(path10.join(outputDir, ".mcp.json"), JSON.stringify({ mcpServers }, null, 2) + "\n", "utf-8");
9313
9506
  }
9314
9507
  }
9315
9508
  if (selectedMcpTools.length > 0) {
@@ -9590,7 +9783,7 @@ function collectLeafPaths(props, prefix, keys) {
9590
9783
  if (!props) return;
9591
9784
  for (const [key, value] of Object.entries(props)) {
9592
9785
  const fullPath = prefix ? `${prefix}.${key}` : key;
9593
- if (value.properties && typeof value.properties === "object") {
9786
+ if (value.properties) {
9594
9787
  collectLeafPaths(value.properties, fullPath, keys);
9595
9788
  } else {
9596
9789
  keys.add(fullPath);
@@ -9656,9 +9849,7 @@ async function setConfigValue(targetDir, key, value, registry2) {
9656
9849
  if (affected.length === 0) {
9657
9850
  return;
9658
9851
  }
9659
- const assetNames = affected.map(
9660
- (t) => t.replace(/\/SKILL\.md\.tmpl$/, "").replace(/\/[^/]+\.md\.tmpl$/, "")
9661
- );
9852
+ const assetNames = affected.map((t) => t.replace(/\/SKILL\.md\.tmpl$/, "").replace(/\/[^/]+\.md\.tmpl$/, ""));
9662
9853
  console.log("");
9663
9854
  console.log("\u68C0\u6D4B\u5230\u914D\u7F6E\u53D8\u66F4\uFF0C\u9700\u8981\u91CD\u65B0\u751F\u6210\u4EE5\u4E0B\u5185\u5BB9\uFF1A");
9664
9855
  for (const name of assetNames) {
@@ -9750,19 +9941,12 @@ function refreshConfig(cliDir2, targetDir, registry2) {
9750
9941
  const targetSkillDir = path15.join(targetDotClaudeDir, "skills", skillId);
9751
9942
  const outputPath = path15.join(targetSkillDir, "SKILL.md");
9752
9943
  try {
9753
- const enrichedVars = injectSkillCapabilities(
9754
- tmplPath,
9755
- {},
9756
- installedMcpServers,
9757
- registry2.mcpTools ?? {}
9758
- );
9944
+ const enrichedVars = injectSkillCapabilities(tmplPath, {}, installedMcpServers, registry2.mcpTools ?? {});
9759
9945
  fs19.mkdirSync(targetSkillDir, { recursive: true });
9760
9946
  renderTemplate(tmplPath, enrichedVars, outputPath);
9761
9947
  result.refreshed.push(skillId);
9762
9948
  } catch (err) {
9763
- result.errors.push(
9764
- `\u5237\u65B0 skill "${skillId}" \u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`
9765
- );
9949
+ result.errors.push(`\u5237\u65B0 skill "${skillId}" \u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`);
9766
9950
  }
9767
9951
  }
9768
9952
  const mcpCapabilities = buildUnifiedMcpCapabilities2(cliDir2, installedMcpServers, registry2.mcpTools ?? {});
@@ -9844,7 +10028,7 @@ function registerConfigCommand(program2, registry2) {
9844
10028
  // src/commands/template-list.ts
9845
10029
  function listTemplates(registry2, tool) {
9846
10030
  const reader = new RegistryReader(registry2);
9847
- const skills = tool ? reader.getToolSkills(tool) : Object.entries(registry2.skills).map(([id, entry]) => ({ id, ...entry }));
10031
+ const skills = tool ? reader.getToolSkills(tool).map((s) => ({ ...s, id: "" })) : Object.entries(registry2.skills).map(([id, entry]) => ({ id, ...entry }));
9848
10032
  if (skills.length === 0) {
9849
10033
  console.log("\u6CA1\u6709\u627E\u5230\u53EF\u7528\u7684\u6A21\u677F");
9850
10034
  return;
@@ -9852,7 +10036,7 @@ function listTemplates(registry2, tool) {
9852
10036
  console.log("\u53EF\u7528\u7684\u6A21\u677F:");
9853
10037
  console.log("");
9854
10038
  for (const skill of skills) {
9855
- const id = "id" in skill ? skill.id : "";
10039
+ const id = skill.id;
9856
10040
  const toolName = skill.tool || "";
9857
10041
  const toolLabel = toolName.charAt(0).toUpperCase() + toolName.slice(1);
9858
10042
  const displayName = skill.name || id;
@@ -9939,10 +10123,7 @@ var fs22 = __toESM(require("node:fs"));
9939
10123
  var path18 = __toESM(require("node:path"));
9940
10124
  function getCliVersion() {
9941
10125
  try {
9942
- const paths = [
9943
- path18.resolve(__dirname, "package.json"),
9944
- path18.resolve(__dirname, "..", "package.json")
9945
- ];
10126
+ const paths = [path18.resolve(__dirname, "package.json"), path18.resolve(__dirname, "..", "package.json")];
9946
10127
  for (const pkgPath of paths) {
9947
10128
  if (fs22.existsSync(pkgPath)) {
9948
10129
  const pkg = JSON.parse(fs22.readFileSync(pkgPath, "utf-8"));
@@ -9999,11 +10180,7 @@ function loadChecksums(targetDir) {
9999
10180
  function saveChecksums(targetDir, checksums) {
10000
10181
  const dir = path19.join(targetDir, ".claude");
10001
10182
  ensureDir3(dir);
10002
- fs23.writeFileSync(
10003
- path19.join(dir, CHECKSUM_FILE),
10004
- JSON.stringify(checksums, null, 2) + "\n",
10005
- "utf-8"
10006
- );
10183
+ fs23.writeFileSync(path19.join(dir, CHECKSUM_FILE), JSON.stringify(checksums, null, 2) + "\n", "utf-8");
10007
10184
  }
10008
10185
  function backupFile(filePath) {
10009
10186
  try {
@@ -10261,8 +10438,10 @@ function upgradeTemplates(cliDir2, targetDir, skillIds) {
10261
10438
  const count = report.syncedSkills.length;
10262
10439
  const agentCount = report.syncedAgents.length;
10263
10440
  const hookCount = report.syncedHooks.length;
10264
- console.log(`
10265
- \u2714 \u540C\u6B65\u5B8C\u6210\uFF1A${count} \u4E2A skill${agentCount > 0 ? `\u3001${agentCount} \u4E2A agent` : ""}${hookCount > 0 ? `\u3001${hookCount} \u4E2A hook` : ""}`);
10441
+ console.log(
10442
+ `
10443
+ \u2714 \u540C\u6B65\u5B8C\u6210\uFF1A${count} \u4E2A skill${agentCount > 0 ? `\u3001${agentCount} \u4E2A agent` : ""}${hookCount > 0 ? `\u3001${hookCount} \u4E2A hook` : ""}`
10444
+ );
10266
10445
  if (report.backedUpFiles.length > 0) {
10267
10446
  console.log(` \u5DF2\u5907\u4EFD ${report.backedUpFiles.length} \u4E2A\u4FEE\u6539\u8FC7\u7684\u6587\u4EF6`);
10268
10447
  }
@@ -10448,7 +10627,11 @@ function migrateOldDataSources(targetDir) {
10448
10627
  }
10449
10628
  const cleaned = remainingLines.join("\n").trimEnd();
10450
10629
  if (cleaned === "") {
10451
- fs24.writeFileSync(envPath, "# \u73AF\u5883 baseURL \u914D\u7F6E\u5DF2\u8FC1\u79FB\u81F3 .deepstorm/settings.json\n# \u8BF7\u52FF\u5728\u6B64\u5904\u91CD\u590D\u5B9A\u4E49 BASE_URL_* \u914D\u7F6E\n", "utf-8");
10630
+ fs24.writeFileSync(
10631
+ envPath,
10632
+ "# \u73AF\u5883 baseURL \u914D\u7F6E\u5DF2\u8FC1\u79FB\u81F3 .deepstorm/settings.json\n# \u8BF7\u52FF\u5728\u6B64\u5904\u91CD\u590D\u5B9A\u4E49 BASE_URL_* \u914D\u7F6E\n",
10633
+ "utf-8"
10634
+ );
10452
10635
  } else if (hasDefaultEnv || Object.keys(baseUrlVars).length > 0) {
10453
10636
  fs24.writeFileSync(envPath, cleaned + "\n", "utf-8");
10454
10637
  }
@@ -10507,13 +10690,453 @@ function registerPilotCommands(program2) {
10507
10690
  (0, import_pilot.registerPilotCommands)(program2);
10508
10691
  }
10509
10692
 
10510
- // src/commands/doctor.ts
10511
- var fs25 = __toESM(require("node:fs"));
10693
+ // src/record/browser-recorder.ts
10694
+ var path22 = __toESM(require("node:path"));
10695
+ var fs26 = __toESM(require("node:fs"));
10696
+ var import_node_child_process3 = require("node:child_process");
10697
+
10698
+ // src/record/recorder.ts
10512
10699
  var path21 = __toESM(require("node:path"));
10700
+ var fs25 = __toESM(require("node:fs"));
10701
+ function filterNoiseEvents(events) {
10702
+ const noiseTypes = [];
10703
+ const noiseSet = new Set(noiseTypes);
10704
+ return events.filter((e) => !noiseSet.has(e.type));
10705
+ }
10706
+ function eventGroupKey(e) {
10707
+ const selector = e.data?.cssSelector || "";
10708
+ return { cssSelector: selector, type: e.type };
10709
+ }
10710
+ function dedupEvents(events) {
10711
+ if (events.length === 0) return [];
10712
+ const result = [];
10713
+ const inputElements = /* @__PURE__ */ new Map();
10714
+ for (const current of events) {
10715
+ if (current.type === "input" || current.type === "select") {
10716
+ const key = eventGroupKey(current);
10717
+ const dedupKey = `${key.cssSelector}|${key.type}`;
10718
+ inputElements.set(dedupKey, current);
10719
+ continue;
10720
+ }
10721
+ if (current.type === "click") {
10722
+ const lastEvent = result.length > 0 ? result[result.length - 1] : null;
10723
+ if (lastEvent && lastEvent.type === "click") {
10724
+ const currentKey = eventGroupKey(current);
10725
+ const lastKey = eventGroupKey(lastEvent);
10726
+ const sameElement = currentKey.cssSelector === lastKey.cssSelector;
10727
+ const withinThreshold = current.ts - lastEvent.ts <= 500;
10728
+ if (sameElement && withinThreshold) {
10729
+ continue;
10730
+ }
10731
+ }
10732
+ }
10733
+ result.push(current);
10734
+ }
10735
+ inputElements.forEach((event) => {
10736
+ result.push(event);
10737
+ });
10738
+ result.sort((a, b) => a.ts - b.ts);
10739
+ return result;
10740
+ }
10741
+ function serializeRecording(data) {
10742
+ return JSON.stringify(data, null, 2);
10743
+ }
10744
+ function buildRecordingFileName(seq = 1) {
10745
+ const now = /* @__PURE__ */ new Date();
10746
+ const pad = (n) => String(n).padStart(2, "0");
10747
+ const dateTime = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
10748
+ const seqStr = seq > 1 ? `-${seq}` : "";
10749
+ return `${dateTime}${seqStr}.recording.json`;
10750
+ }
10751
+ function recordingsDir() {
10752
+ return path21.join(process.cwd(), ".deepstorm", "recordings");
10753
+ }
10754
+ function getNextSeq(datePrefix) {
10755
+ const dir = recordingsDir();
10756
+ if (!fs25.existsSync(dir)) return 1;
10757
+ const files = fs25.readdirSync(dir).filter((f) => f.startsWith(datePrefix));
10758
+ if (files.length === 0) return 1;
10759
+ let maxSeq = 0;
10760
+ for (const f of files) {
10761
+ const match = f.match(/(?:-(\d+))?\.recording\.json$/);
10762
+ if (match) {
10763
+ const s = match[1] ? parseInt(match[1], 10) : 1;
10764
+ maxSeq = Math.max(maxSeq, s);
10765
+ }
10766
+ }
10767
+ return maxSeq + 1;
10768
+ }
10769
+
10770
+ // src/record/browser-recorder.ts
10771
+ var playwrightModule = null;
10772
+ async function getPlaywright() {
10773
+ if (!playwrightModule) {
10774
+ playwrightModule = await import("playwright");
10775
+ }
10776
+ return playwrightModule;
10777
+ }
10778
+ function isPlaywrightPackageInstalled() {
10779
+ try {
10780
+ require.resolve("playwright");
10781
+ return true;
10782
+ } catch {
10783
+ return false;
10784
+ }
10785
+ }
10786
+ async function ensurePlaywrightPackage() {
10787
+ if (isPlaywrightPackageInstalled()) return;
10788
+ console.log("\u{1F4E6} \u672A\u68C0\u6D4B\u5230 Playwright\uFF0C\u6B63\u5728\u81EA\u52A8\u5B89\u88C5...");
10789
+ const targetDir = process.cwd();
10790
+ try {
10791
+ (0, import_node_child_process3.execSync)("npm install playwright@latest --no-save", {
10792
+ cwd: targetDir,
10793
+ stdio: "inherit",
10794
+ env: { ...process.env, NODE_ENV: void 0 }
10795
+ });
10796
+ } catch {
10797
+ console.log("npm install \u5931\u8D25\uFF0C\u5C1D\u8BD5\u901A\u8FC7 npx \u5B89\u88C5...");
10798
+ (0, import_node_child_process3.execSync)("npx --yes playwright@latest install --dry-run", {
10799
+ stdio: "inherit"
10800
+ });
10801
+ }
10802
+ if (!isPlaywrightPackageInstalled()) {
10803
+ console.error("\u274C Playwright \u81EA\u52A8\u5B89\u88C5\u5931\u8D25\u3002\u8BF7\u624B\u52A8\u6267\u884C:");
10804
+ console.error(" npm install playwright");
10805
+ process.exit(1);
10806
+ }
10807
+ console.log("\u2705 Playwright \u5B89\u88C5\u6210\u529F");
10808
+ }
10809
+ async function isPlaywrightBrowserAvailable() {
10810
+ try {
10811
+ const pw = await getPlaywright();
10812
+ const browser = await pw.chromium.launch({ headless: true });
10813
+ await browser.close();
10814
+ return true;
10815
+ } catch {
10816
+ return false;
10817
+ }
10818
+ }
10819
+ async function ensurePlaywrightBrowsers() {
10820
+ if (await isPlaywrightBrowserAvailable()) return;
10821
+ console.log("\u{1F310} \u672A\u68C0\u6D4B\u5230 Playwright Chromium \u6D4F\u89C8\u5668\uFF0C\u6B63\u5728\u81EA\u52A8\u5B89\u88C5...");
10822
+ try {
10823
+ const pw = await getPlaywright();
10824
+ await pw.chromium.launch({ headless: true, executablePath: void 0 });
10825
+ } catch {
10826
+ try {
10827
+ (0, import_node_child_process3.execSync)("npx playwright install chromium", {
10828
+ stdio: "inherit",
10829
+ env: { ...process.env, PLAYWRIGHT_BROWSERS_PATH: "0" }
10830
+ });
10831
+ } catch {
10832
+ console.error("\u274C \u6D4F\u89C8\u5668\u81EA\u52A8\u5B89\u88C5\u5931\u8D25\u3002\u8BF7\u624B\u52A8\u6267\u884C:");
10833
+ console.error(" npx playwright install chromium");
10834
+ console.error("");
10835
+ console.error(" \u6216\u6307\u5B9A\u5DF2\u5B89\u88C5\u7684\u6D4F\u89C8\u5668\u8DEF\u5F84:");
10836
+ console.error(" export PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/path/to/chrome");
10837
+ process.exit(1);
10838
+ }
10839
+ }
10840
+ if (await isPlaywrightBrowserAvailable()) {
10841
+ console.log("\u2705 \u6D4F\u89C8\u5668\u5B89\u88C5\u6210\u529F");
10842
+ } else {
10843
+ console.error("\u274C \u6D4F\u89C8\u5668\u5B89\u88C5\u540E\u4ECD\u7136\u4E0D\u53EF\u7528\uFF0C\u8BF7\u624B\u52A8\u6267\u884C: npx playwright install chromium");
10844
+ process.exit(1);
10845
+ }
10846
+ }
10847
+ async function recordBrowserSession(url) {
10848
+ let targetUrl;
10849
+ try {
10850
+ targetUrl = new URL(url);
10851
+ } catch {
10852
+ throw new Error(`\u65E0\u6548\u7684 URL: ${url}`);
10853
+ }
10854
+ await ensurePlaywrightPackage();
10855
+ await ensurePlaywrightBrowsers();
10856
+ const pw = await getPlaywright();
10857
+ const startTime = Date.now();
10858
+ const browser = await pw.chromium.launch({ headless: false });
10859
+ const context = await browser.newContext();
10860
+ const page = await context.newPage();
10861
+ const events = [];
10862
+ const requestMethods = /* @__PURE__ */ new Map();
10863
+ const cdpSession = await context.newCDPSession(page);
10864
+ cdpSession.on("Network.requestWillBeSent", (params) => {
10865
+ const { request, type, requestId } = params;
10866
+ requestMethods.set(requestId, request.method);
10867
+ if (type === "XHR" || type === "Fetch") {
10868
+ events.push({
10869
+ ts: Date.now() - startTime,
10870
+ type: "network",
10871
+ data: {
10872
+ url: request.url,
10873
+ method: request.method,
10874
+ type: "request"
10875
+ }
10876
+ });
10877
+ }
10878
+ });
10879
+ cdpSession.on("Network.responseReceived", (params) => {
10880
+ const { response, type, requestId } = params;
10881
+ const method = requestMethods.get(requestId) ?? "GET";
10882
+ if (type === "XHR" || type === "Fetch") {
10883
+ events.push({
10884
+ ts: Date.now() - startTime,
10885
+ type: "network",
10886
+ data: {
10887
+ url: response.url,
10888
+ method,
10889
+ statusCode: response.status,
10890
+ type: "response"
10891
+ }
10892
+ });
10893
+ }
10894
+ });
10895
+ cdpSession.on("Page.frameNavigated", (params) => {
10896
+ const { frame } = params;
10897
+ if (frame.parentId === void 0 || frame.parentId === null) {
10898
+ events.push({
10899
+ ts: Date.now() - startTime,
10900
+ type: "navigation",
10901
+ data: {
10902
+ url: frame.url,
10903
+ title: frame.name || frame.url,
10904
+ type: "full"
10905
+ }
10906
+ });
10907
+ }
10908
+ });
10909
+ cdpSession.on("Page.navigatedWithinDocument", (params) => {
10910
+ events.push({
10911
+ ts: Date.now() - startTime,
10912
+ type: "navigation",
10913
+ data: {
10914
+ url: params.url,
10915
+ type: "spa"
10916
+ }
10917
+ });
10918
+ });
10919
+ await page.exposeFunction("__deepstorm_recordEvent", (type, data) => {
10920
+ events.push({
10921
+ ts: Date.now() - startTime,
10922
+ type,
10923
+ data
10924
+ });
10925
+ });
10926
+ await page.goto(targetUrl.href);
10927
+ await page.evaluate(`(function() {
10928
+ var send = window.__deepstorm_recordEvent;
10929
+
10930
+ // Click listener (capture phase)
10931
+ document.addEventListener('click', function(e) {
10932
+ var target = e.target;
10933
+ if (!target || target === document.body || target === document.documentElement) return;
10934
+
10935
+ var info = {
10936
+ tagName: target.tagName.toLowerCase(),
10937
+ id: target.id || undefined,
10938
+ className: (target.className || '').toString().slice(0, 100) || undefined,
10939
+ textContent: (target.textContent || '').trim().slice(0, 100) || undefined
10940
+ };
10941
+
10942
+ var role = target.getAttribute('role');
10943
+ if (role) info.role = role;
10944
+ var ariaLabel = target.getAttribute('aria-label');
10945
+ if (ariaLabel) info.ariaLabel = ariaLabel;
10946
+ var placeholder = target.getAttribute('placeholder');
10947
+ if (placeholder) info.placeholder = placeholder;
10948
+ var dataTestId = target.getAttribute('data-testid');
10949
+ if (dataTestId) info.dataTestId = dataTestId;
10950
+
10951
+ info.cssSelector = buildSimpleSelector(target);
10952
+ send('click', info);
10953
+ }, true);
10954
+
10955
+ // Input listener (change event for final value)
10956
+ document.addEventListener('change', function(e) {
10957
+ var target = e.target;
10958
+ if (!target) return;
10959
+
10960
+ if (target.tagName === 'SELECT') {
10961
+ send('select', {
10962
+ tagName: 'select',
10963
+ value: target.value,
10964
+ name: target.name || undefined,
10965
+ cssSelector: buildSimpleSelector(target)
10966
+ });
10967
+ return;
10968
+ }
10969
+
10970
+ if (target.type === 'password') {
10971
+ send('input', {
10972
+ tagName: target.tagName.toLowerCase(),
10973
+ type: 'password',
10974
+ value: '****',
10975
+ name: target.name || undefined,
10976
+ placeholder: target.placeholder || undefined,
10977
+ cssSelector: buildSimpleSelector(target)
10978
+ });
10979
+ return;
10980
+ }
10981
+
10982
+ send('input', {
10983
+ tagName: target.tagName.toLowerCase(),
10984
+ type: target.type || undefined,
10985
+ value: target.value,
10986
+ name: target.name || undefined,
10987
+ placeholder: target.placeholder || undefined,
10988
+ cssSelector: buildSimpleSelector(target)
10989
+ });
10990
+ }, true);
10991
+
10992
+ // Submit listener
10993
+ document.addEventListener('submit', function(e) {
10994
+ var form = e.target;
10995
+ if (!form) return;
10996
+ send('submit', {
10997
+ cssSelector: buildSimpleSelector(form),
10998
+ id: form.id || undefined,
10999
+ action: form.action || undefined
11000
+ });
11001
+ }, true);
11002
+
11003
+ // Helper: build simple CSS selector
11004
+ function buildSimpleSelector(el) {
11005
+ if (el.id) return '#' + CSS.escape(el.id);
11006
+ var testId = el.getAttribute('data-testid');
11007
+ if (testId) return '[data-testid="' + CSS.escape(testId) + '"]';
11008
+ var tag = el.tagName.toLowerCase();
11009
+ var cls = '';
11010
+ for (var i = 0; i < el.classList.length; i++) {
11011
+ cls += '.' + CSS.escape(el.classList[i]);
11012
+ }
11013
+ if (cls) return tag + cls;
11014
+ var parent = el.parentElement;
11015
+ if (parent) {
11016
+ var siblings = Array.from(parent.children).filter(function(s) {
11017
+ return s.tagName === el.tagName;
11018
+ });
11019
+ if (siblings.length > 1) {
11020
+ var idx = siblings.indexOf(el) + 1;
11021
+ return tag + ':nth-child(' + idx + ')';
11022
+ }
11023
+ }
11024
+ return tag;
11025
+ }
11026
+ })();`);
11027
+ console.log("\u{1F3A5} \u5F55\u5236\u4E2D... \u6309 Ctrl+D \u505C\u6B62");
11028
+ const stopPromise = new Promise((resolve4) => {
11029
+ let rawModeSupported = false;
11030
+ try {
11031
+ process.stdin.setRawMode?.(true);
11032
+ rawModeSupported = true;
11033
+ } catch {
11034
+ console.log("\u24D8 \u5F53\u524D\u7EC8\u7AEF\u4E0D\u652F\u6301 raw mode\uFF0C\u8BF7\u5173\u95ED\u6D4F\u89C8\u5668\u7A97\u53E3\u505C\u6B62\u5F55\u5236");
11035
+ }
11036
+ process.stdin.resume();
11037
+ process.stdin.on("data", (chunk) => {
11038
+ if (chunk.length === 1 && chunk[0] === 4) {
11039
+ cleanup();
11040
+ resolve4();
11041
+ }
11042
+ });
11043
+ process.stdin.on("end", () => {
11044
+ cleanup();
11045
+ resolve4();
11046
+ });
11047
+ function cleanup() {
11048
+ if (rawModeSupported) {
11049
+ try {
11050
+ process.stdin.setRawMode?.(false);
11051
+ } catch {
11052
+ }
11053
+ }
11054
+ process.stdin.pause();
11055
+ }
11056
+ });
11057
+ const crashPromise = new Promise((_resolve, reject) => {
11058
+ browser.on("disconnected", () => {
11059
+ reject(new Error("\u6D4F\u89C8\u5668\u8FDE\u63A5\u65AD\u5F00"));
11060
+ });
11061
+ });
11062
+ try {
11063
+ await Promise.race([stopPromise, crashPromise]);
11064
+ } catch (err) {
11065
+ console.error("\n\u6D4F\u89C8\u5668\u8FDE\u63A5\u65AD\u5F00\uFF0C\u5F55\u5236\u5DF2\u7EC8\u6B62");
11066
+ throw err;
11067
+ } finally {
11068
+ try {
11069
+ process.stdin.setRawMode?.(false);
11070
+ } catch {
11071
+ }
11072
+ process.stdin.pause();
11073
+ }
11074
+ const duration = Date.now() - startTime;
11075
+ try {
11076
+ const screenshotBuffer = await page.screenshot({ type: "jpeg", quality: 70 });
11077
+ events.push({
11078
+ ts: Date.now() - startTime,
11079
+ type: "screenshot",
11080
+ data: {
11081
+ label: "final",
11082
+ data: screenshotBuffer.toString("base64")
11083
+ }
11084
+ });
11085
+ } catch {
11086
+ }
11087
+ await browser.close();
11088
+ const filteredEvents = dedupEvents(filterNoiseEvents(events));
11089
+ const recordingData = {
11090
+ meta: {
11091
+ url: targetUrl.href,
11092
+ recordedAt: new Date(startTime).toISOString(),
11093
+ duration,
11094
+ browser: `Playwright Chromium ${browser.version()}`
11095
+ },
11096
+ events: filteredEvents
11097
+ };
11098
+ const dir = recordingsDir();
11099
+ fs26.mkdirSync(dir, { recursive: true });
11100
+ const datePrefix = buildRecordingFileName().replace(".recording.json", "");
11101
+ const seq = getNextSeq(datePrefix);
11102
+ const fileName = buildRecordingFileName(seq);
11103
+ const filePath = path22.join(dir, fileName);
11104
+ fs26.writeFileSync(filePath, serializeRecording(recordingData), "utf-8");
11105
+ console.log(`
11106
+ \u2705 \u5F55\u5236\u5B8C\u6210`);
11107
+ console.log(` \u6301\u7EED\u65F6\u95F4: ${(duration / 1e3).toFixed(1)} \u79D2`);
11108
+ console.log(` \u4E8B\u4EF6\u603B\u6570: ${filteredEvents.length}`);
11109
+ console.log(` \u6587\u4EF6\u8DEF\u5F84: ${filePath}`);
11110
+ return filePath;
11111
+ }
11112
+
11113
+ // src/commands/record.ts
11114
+ function registerRecordCommand(program2) {
11115
+ const recordCmd = program2.command("record").description("\u6D4F\u89C8\u5668\u64CD\u4F5C\u5F55\u5236 \u2014 \u542F\u52A8 headed \u6D4F\u89C8\u5668\u5F55\u5236\u7528\u6237\u64CD\u4F5C\uFF0C\u8F93\u51FA .recording.json");
11116
+ recordCmd.command("start").description("\u5F00\u59CB\u5F55\u5236 \u2014 \u542F\u52A8 headed \u6D4F\u89C8\u5668\u5E76\u6253\u5F00\u6307\u5B9A URL").option("-u, --url <url>", "\u76EE\u6807 URL").action(async (options) => {
11117
+ if (!options.url) {
11118
+ console.error("\u8BF7\u4F7F\u7528 -u <url> \u6307\u5B9A\u76EE\u6807 URL");
11119
+ process.exit(1);
11120
+ }
11121
+ try {
11122
+ await recordBrowserSession(options.url);
11123
+ } catch (err) {
11124
+ console.error("\u5F55\u5236\u5931\u8D25:", err instanceof Error ? err.message : err);
11125
+ process.exit(1);
11126
+ }
11127
+ });
11128
+ recordCmd.command("stop").description("\u505C\u6B62\u5F55\u5236 \u2014 \u505C\u6B62\u5F53\u524D\u5F55\u5236\u5E76\u5199\u5165\u6587\u4EF6").action(() => {
11129
+ console.log("\u8BF7\u5728\u5F55\u5236\u7EC8\u7AEF\u6309 Ctrl+D \u505C\u6B62\u5F55\u5236");
11130
+ });
11131
+ }
11132
+
11133
+ // src/commands/doctor.ts
11134
+ var fs27 = __toESM(require("node:fs"));
11135
+ var path23 = __toESM(require("node:path"));
10513
11136
  function getInstalledSkills(targetDir) {
10514
11137
  const settingsPath = getDeepStormConfigPath(targetDir);
10515
11138
  try {
10516
- const settings = JSON.parse(fs25.readFileSync(settingsPath, "utf-8"));
11139
+ const settings = JSON.parse(fs27.readFileSync(settingsPath, "utf-8"));
10517
11140
  return settings.installedSkills || [];
10518
11141
  } catch {
10519
11142
  return [];
@@ -10530,7 +11153,7 @@ function runDoctor(targetDir) {
10530
11153
  message: `v${version}\uFF08\u8FD0\u884C deepstorm update \u68C0\u67E5\u6700\u65B0\u7248\u672C\uFF09`
10531
11154
  });
10532
11155
  let hasDeepStorm = false;
10533
- if (!fs25.existsSync(settingsPath)) {
11156
+ if (!fs27.existsSync(settingsPath)) {
10534
11157
  checks.push({
10535
11158
  name: "\u914D\u7F6E\u6587\u4EF6",
10536
11159
  status: "warn",
@@ -10538,7 +11161,7 @@ function runDoctor(targetDir) {
10538
11161
  });
10539
11162
  } else {
10540
11163
  try {
10541
- const config = JSON.parse(fs25.readFileSync(settingsPath, "utf-8"));
11164
+ const config = JSON.parse(fs27.readFileSync(settingsPath, "utf-8"));
10542
11165
  if (config && Object.keys(config).length > 0) {
10543
11166
  hasDeepStorm = true;
10544
11167
  checks.push({
@@ -10561,20 +11184,18 @@ function runDoctor(targetDir) {
10561
11184
  });
10562
11185
  }
10563
11186
  }
10564
- const skillsDir = path21.join(targetDir, ".claude", "skills");
10565
- if (fs25.existsSync(skillsDir)) {
10566
- const skillDirs = fs25.readdirSync(skillsDir).filter(
10567
- (n) => fs25.statSync(path21.join(skillsDir, n)).isDirectory()
10568
- );
11187
+ const skillsDir = path23.join(targetDir, ".claude", "skills");
11188
+ if (fs27.existsSync(skillsDir)) {
11189
+ const skillDirs = fs27.readdirSync(skillsDir).filter((n) => fs27.statSync(path23.join(skillsDir, n)).isDirectory());
10569
11190
  let validCount = 0;
10570
11191
  let invalidCount = 0;
10571
11192
  for (const skillName of skillDirs) {
10572
- const skillMd = path21.join(skillsDir, skillName, "SKILL.md");
10573
- if (!fs25.existsSync(skillMd)) {
11193
+ const skillMd = path23.join(skillsDir, skillName, "SKILL.md");
11194
+ if (!fs27.existsSync(skillMd)) {
10574
11195
  invalidCount++;
10575
11196
  continue;
10576
11197
  }
10577
- const content = fs25.readFileSync(skillMd, "utf-8");
11198
+ const content = fs27.readFileSync(skillMd, "utf-8");
10578
11199
  const frontmatter = parseFrontmatter(content);
10579
11200
  if (frontmatter && frontmatter.name) {
10580
11201
  validCount++;
@@ -10604,7 +11225,7 @@ function runDoctor(targetDir) {
10604
11225
  const installedSkills = getInstalledSkills(targetDir);
10605
11226
  const missingSkills = [];
10606
11227
  for (const skillId of installedSkills) {
10607
- if (!fs25.existsSync(path21.join(skillsDir, skillId))) {
11228
+ if (!fs27.existsSync(path23.join(skillsDir, skillId))) {
10608
11229
  missingSkills.push(skillId);
10609
11230
  }
10610
11231
  }
@@ -10628,10 +11249,10 @@ function runDoctor(targetDir) {
10628
11249
  message: ".claude/skills/ \u4E0D\u5B58\u5728"
10629
11250
  });
10630
11251
  }
10631
- const mcpPath = path21.join(targetDir, ".mcp.json");
10632
- if (fs25.existsSync(mcpPath)) {
11252
+ const mcpPath = path23.join(targetDir, ".mcp.json");
11253
+ if (fs27.existsSync(mcpPath)) {
10633
11254
  try {
10634
- const mcp = JSON.parse(fs25.readFileSync(mcpPath, "utf-8"));
11255
+ const mcp = JSON.parse(fs27.readFileSync(mcpPath, "utf-8"));
10635
11256
  const hasServers = mcp.mcpServers && Object.keys(mcp.mcpServers).length > 0;
10636
11257
  checks.push({
10637
11258
  name: "MCP \u670D\u52A1\u5668",
@@ -10652,12 +11273,12 @@ function runDoctor(targetDir) {
10652
11273
  message: ".mcp.json \u4E0D\u5B58\u5728"
10653
11274
  });
10654
11275
  }
10655
- if (hasDeepStorm && fs25.existsSync(settingsPath)) {
11276
+ if (hasDeepStorm && fs27.existsSync(settingsPath)) {
10656
11277
  try {
10657
- const config = JSON.parse(fs25.readFileSync(settingsPath, "utf-8"));
11278
+ const config = JSON.parse(fs27.readFileSync(settingsPath, "utf-8"));
10658
11279
  const installedMcp = config.installedMcpServers;
10659
11280
  if (installedMcp && installedMcp.length > 0) {
10660
- const mcpExists = fs25.existsSync(mcpPath);
11281
+ const mcpExists = fs27.existsSync(mcpPath);
10661
11282
  if (!mcpExists) {
10662
11283
  checks.push({
10663
11284
  name: "MCP \u4E00\u81F4\u6027",
@@ -10666,7 +11287,7 @@ function runDoctor(targetDir) {
10666
11287
  });
10667
11288
  } else {
10668
11289
  try {
10669
- const mcpConfig = JSON.parse(fs25.readFileSync(mcpPath, "utf-8"));
11290
+ const mcpConfig = JSON.parse(fs27.readFileSync(mcpPath, "utf-8"));
10670
11291
  const servers = mcpConfig.mcpServers || {};
10671
11292
  const missing = [];
10672
11293
  for (const toolName of installedMcp) {
@@ -10694,9 +11315,9 @@ function runDoctor(targetDir) {
10694
11315
  } catch {
10695
11316
  }
10696
11317
  }
10697
- if (hasDeepStorm && fs25.existsSync(settingsPath)) {
11318
+ if (hasDeepStorm && fs27.existsSync(settingsPath)) {
10698
11319
  try {
10699
- const config = JSON.parse(fs25.readFileSync(settingsPath, "utf-8"));
11320
+ const config = JSON.parse(fs27.readFileSync(settingsPath, "utf-8"));
10700
11321
  const validKeys = loadValidConfigKeys();
10701
11322
  if (validKeys.size > 0 && config) {
10702
11323
  const configKeys = [];
@@ -10749,18 +11370,18 @@ function printDoctorReport(report) {
10749
11370
  }
10750
11371
 
10751
11372
  // src/commands/uninstall.ts
10752
- var fs26 = __toESM(require("node:fs"));
10753
- var path22 = __toESM(require("node:path"));
11373
+ var fs28 = __toESM(require("node:fs"));
11374
+ var path24 = __toESM(require("node:path"));
10754
11375
  var import_prompts4 = require("@clack/prompts");
10755
11376
  async function uninstallDeepStorm(targetDir) {
10756
11377
  const settingsPath = getDeepStormConfigPath(targetDir);
10757
- if (!fs26.existsSync(settingsPath)) {
11378
+ if (!fs28.existsSync(settingsPath)) {
10758
11379
  console.log("DeepStorm \u5C1A\u672A\u914D\u7F6E\uFF0C\u65E0\u9700\u5378\u8F7D");
10759
11380
  return;
10760
11381
  }
10761
11382
  let hasDeepStorm = false;
10762
11383
  try {
10763
- const settings = JSON.parse(fs26.readFileSync(settingsPath, "utf-8"));
11384
+ const settings = JSON.parse(fs28.readFileSync(settingsPath, "utf-8"));
10764
11385
  hasDeepStorm = !!settings && Object.keys(settings).length > 0;
10765
11386
  } catch {
10766
11387
  }
@@ -10770,14 +11391,14 @@ async function uninstallDeepStorm(targetDir) {
10770
11391
  }
10771
11392
  cleanInstalled(targetDir);
10772
11393
  resetConfig(targetDir);
10773
- const templatesDir = path22.join(targetDir, ".deepstorm", "templates");
10774
- if (fs26.existsSync(templatesDir)) {
11394
+ const templatesDir = path24.join(targetDir, ".deepstorm", "templates");
11395
+ if (fs28.existsSync(templatesDir)) {
10775
11396
  const shouldDelete = await (0, import_prompts4.confirm)({
10776
11397
  message: "\u662F\u5426\u5220\u9664 .deepstorm/templates/ \u76EE\u5F55\uFF1F",
10777
11398
  initialValue: false
10778
11399
  });
10779
11400
  if (shouldDelete) {
10780
- fs26.rmSync(templatesDir, { recursive: true, force: true });
11401
+ fs28.rmSync(templatesDir, { recursive: true, force: true });
10781
11402
  console.log("\u2714 .deepstorm/templates/ \u5DF2\u5220\u9664");
10782
11403
  } else {
10783
11404
  console.log("\u4FDD\u7559 .deepstorm/templates/");
@@ -10790,9 +11411,9 @@ async function uninstallDeepStorm(targetDir) {
10790
11411
  var program = new import_commander.Command();
10791
11412
  program.name("deepstorm").description("DeepStorm CLI \u2014 \u4E00\u952E\u914D\u7F6E\u9879\u76EE\u5F00\u53D1\u73AF\u5883").version(getCliVersion(), "-v, --version", "\u8F93\u51FA\u7248\u672C\u53F7");
10792
11413
  function loadRegistry() {
10793
- const registryPath = path23.join(__dirname, "registry.json");
11414
+ const registryPath = path25.join(__dirname, "registry.json");
10794
11415
  try {
10795
- const raw = fs27.readFileSync(registryPath, "utf-8");
11416
+ const raw = fs29.readFileSync(registryPath, "utf-8");
10796
11417
  return JSON.parse(raw);
10797
11418
  } catch {
10798
11419
  return { version: "1", tools: {}, wizards: {}, skills: {} };
@@ -10806,6 +11427,7 @@ registerConfigCommand(program, registry);
10806
11427
  registerTemplateCommand(program, registry);
10807
11428
  registerUpdateCommand(program);
10808
11429
  registerPilotCommands(program);
11430
+ registerRecordCommand(program);
10809
11431
  program.command("doctor").description("\u8BCA\u65AD\u9879\u76EE DeepStorm \u914D\u7F6E\u72B6\u6001").action(() => {
10810
11432
  const report = runDoctor(process.cwd());
10811
11433
  printDoctorReport(report);