@namewta/speculo 0.7.3 → 0.7.5

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 (199) hide show
  1. package/README.md +2 -2
  2. package/dist/src/migrations.js +190 -39
  3. package/dist/src/migrations.js.map +1 -1
  4. package/package.json +1 -1
  5. package/template/canonical/README.md +208 -63
  6. package/template/canonical/canonical-person-steelman-deliberation.md +518 -0
  7. package/template/canonical/canonical-specdev-engineering-cognitive-mentor.md +51 -15
  8. package/template/canonical/canonical-specdev-goal-plan.md +183 -116
  9. package/template/canonical/canonical-specdev-grill-with-docs.md +51 -15
  10. package/template/canonical/canonical-specdev-spec.md +61 -21
  11. package/template/canonical/canonical-specdev-tickets.md +83 -43
  12. package/template/canonical/canonical-specdev-wayfinder.md +51 -15
  13. package/template/skills/engineering-standards-builder/README.md +43 -0
  14. package/template/skills/engineering-standards-builder/SKILL.md +233 -0
  15. package/template/skills/engineering-standards-builder/examples/README.md +15 -0
  16. package/template/skills/engineering-standards-builder/examples/fallback/kotlin-gradle/build.gradle.kts +5 -0
  17. package/template/skills/engineering-standards-builder/examples/fallback/kotlin-gradle/expected.json +12 -0
  18. package/template/skills/engineering-standards-builder/examples/fallback/kotlin-gradle/src/main/kotlin/example/App.kt +2 -0
  19. package/template/skills/engineering-standards-builder/examples/go/service/.golangci.yml +3 -0
  20. package/template/skills/engineering-standards-builder/examples/go/service/cmd/api/main.go +3 -0
  21. package/template/skills/engineering-standards-builder/examples/go/service/expected.json +23 -0
  22. package/template/skills/engineering-standards-builder/examples/go/service/go.mod +3 -0
  23. package/template/skills/engineering-standards-builder/examples/go/service/internal/service/service.go +2 -0
  24. package/template/skills/engineering-standards-builder/examples/go/service/internal/service/service_test.go +3 -0
  25. package/template/skills/engineering-standards-builder/examples/java/spring-boot-maven/expected.json +22 -0
  26. package/template/skills/engineering-standards-builder/examples/java/spring-boot-maven/pom.xml +12 -0
  27. package/template/skills/engineering-standards-builder/examples/java/spring-boot-maven/src/main/java/dev/speculo/orders/OrdersApplication.java +7 -0
  28. package/template/skills/engineering-standards-builder/examples/java/spring-boot-maven/src/test/java/dev/speculo/orders/OrdersApplicationTest.java +3 -0
  29. package/template/skills/engineering-standards-builder/examples/polyglot/monorepo/.github/workflows/ci.yml +11 -0
  30. package/template/skills/engineering-standards-builder/examples/polyglot/monorepo/apps/web/package.json +11 -0
  31. package/template/skills/engineering-standards-builder/examples/polyglot/monorepo/apps/web/src/App.vue +2 -0
  32. package/template/skills/engineering-standards-builder/examples/polyglot/monorepo/apps/web/tsconfig.json +8 -0
  33. package/template/skills/engineering-standards-builder/examples/polyglot/monorepo/expected.json +22 -0
  34. package/template/skills/engineering-standards-builder/examples/polyglot/monorepo/package.json +11 -0
  35. package/template/skills/engineering-standards-builder/examples/polyglot/monorepo/pnpm-workspace.yaml +2 -0
  36. package/template/skills/engineering-standards-builder/examples/polyglot/monorepo/services/orders/pom.xml +12 -0
  37. package/template/skills/engineering-standards-builder/examples/polyglot/monorepo/services/orders/src/main/java/dev/speculo/orders/OrdersApplication.java +7 -0
  38. package/template/skills/engineering-standards-builder/examples/polyglot/monorepo/services/worker/go.mod +3 -0
  39. package/template/skills/engineering-standards-builder/examples/polyglot/monorepo/services/worker/main.go +2 -0
  40. package/template/skills/engineering-standards-builder/examples/rust/workspace/Cargo.toml +7 -0
  41. package/template/skills/engineering-standards-builder/examples/rust/workspace/crates/cli/Cargo.toml +8 -0
  42. package/template/skills/engineering-standards-builder/examples/rust/workspace/crates/cli/src/main.rs +1 -0
  43. package/template/skills/engineering-standards-builder/examples/rust/workspace/crates/core/Cargo.toml +5 -0
  44. package/template/skills/engineering-standards-builder/examples/rust/workspace/crates/core/src/lib.rs +3 -0
  45. package/template/skills/engineering-standards-builder/examples/rust/workspace/expected.json +14 -0
  46. package/template/skills/engineering-standards-builder/examples/rust/workspace/rust-toolchain.toml +3 -0
  47. package/template/skills/engineering-standards-builder/examples/typescript/react-vite/expected.json +21 -0
  48. package/template/skills/engineering-standards-builder/examples/typescript/react-vite/package.json +20 -0
  49. package/template/skills/engineering-standards-builder/examples/typescript/react-vite/src/App.test.tsx +2 -0
  50. package/template/skills/engineering-standards-builder/examples/typescript/react-vite/src/App.tsx +1 -0
  51. package/template/skills/engineering-standards-builder/examples/typescript/react-vite/src/main.tsx +3 -0
  52. package/template/skills/engineering-standards-builder/examples/typescript/react-vite/tsconfig.json +10 -0
  53. package/template/skills/engineering-standards-builder/examples/typescript/vue-vite/expected.json +22 -0
  54. package/template/skills/engineering-standards-builder/examples/typescript/vue-vite/package.json +21 -0
  55. package/template/skills/engineering-standards-builder/examples/typescript/vue-vite/src/App.test.ts +2 -0
  56. package/template/skills/engineering-standards-builder/examples/typescript/vue-vite/src/App.vue +7 -0
  57. package/template/skills/engineering-standards-builder/examples/typescript/vue-vite/src/main.ts +4 -0
  58. package/template/skills/engineering-standards-builder/examples/typescript/vue-vite/tsconfig.json +11 -0
  59. package/template/skills/engineering-standards-builder/examples/typescript/vue-vite/vite.config.ts +2 -0
  60. package/template/skills/engineering-standards-builder/manifest.txt +112 -0
  61. package/template/skills/engineering-standards-builder/references/go/00-detection-and-scope.md +37 -0
  62. package/template/skills/engineering-standards-builder/references/go/01-language-and-idioms.md +34 -0
  63. package/template/skills/engineering-standards-builder/references/go/02-modules-packages-and-layout.md +34 -0
  64. package/template/skills/engineering-standards-builder/references/go/03-errors-context-and-concurrency.md +38 -0
  65. package/template/skills/engineering-standards-builder/references/go/04-testing-tooling-quality-gates.md +46 -0
  66. package/template/skills/engineering-standards-builder/references/go/README.md +9 -0
  67. package/template/skills/engineering-standards-builder/references/java/00-detection-and-scope.md +40 -0
  68. package/template/skills/engineering-standards-builder/references/java/01-language-packages-and-api.md +35 -0
  69. package/template/skills/engineering-standards-builder/references/java/02-build-modules-and-dependencies.md +39 -0
  70. package/template/skills/engineering-standards-builder/references/java/03-errors-nullability-resources-concurrency.md +36 -0
  71. package/template/skills/engineering-standards-builder/references/java/04-testing-tooling-quality-gates.md +36 -0
  72. package/template/skills/engineering-standards-builder/references/java/README.md +10 -0
  73. package/template/skills/engineering-standards-builder/references/java/frameworks/spring-boot.md +74 -0
  74. package/template/skills/engineering-standards-builder/references/rules/00-governance-and-precedence.md +52 -0
  75. package/template/skills/engineering-standards-builder/references/rules/01-project-discovery.md +79 -0
  76. package/template/skills/engineering-standards-builder/references/rules/02-evidence-topology-and-scope.md +87 -0
  77. package/template/skills/engineering-standards-builder/references/rules/03-interview-and-decisions.md +77 -0
  78. package/template/skills/engineering-standards-builder/references/rules/04-architecture-and-dependency-boundaries.md +58 -0
  79. package/template/skills/engineering-standards-builder/references/rules/05-files-directories-and-naming.md +53 -0
  80. package/template/skills/engineering-standards-builder/references/rules/06-apis-errors-resources-and-concurrency.md +53 -0
  81. package/template/skills/engineering-standards-builder/references/rules/07-documentation-and-comments.md +46 -0
  82. package/template/skills/engineering-standards-builder/references/rules/08-testing-strategy.md +45 -0
  83. package/template/skills/engineering-standards-builder/references/rules/09-security-configuration-and-data.md +44 -0
  84. package/template/skills/engineering-standards-builder/references/rules/10-performance-observability-and-i18n.md +36 -0
  85. package/template/skills/engineering-standards-builder/references/rules/11-tooling-quality-gates-and-ci.md +69 -0
  86. package/template/skills/engineering-standards-builder/references/rules/12-git-review-and-delivery.md +46 -0
  87. package/template/skills/engineering-standards-builder/references/rules/13-adoption-exceptions-and-ratchets.md +60 -0
  88. package/template/skills/engineering-standards-builder/references/rules/14-generation-contract.md +105 -0
  89. package/template/skills/engineering-standards-builder/references/rules/15-validation-contract.md +65 -0
  90. package/template/skills/engineering-standards-builder/references/rules/16-language-adapter-contract.md +55 -0
  91. package/template/skills/engineering-standards-builder/references/rules/README.md +23 -0
  92. package/template/skills/engineering-standards-builder/references/rust/00-detection-and-scope.md +38 -0
  93. package/template/skills/engineering-standards-builder/references/rust/01-language-api-and-safety.md +35 -0
  94. package/template/skills/engineering-standards-builder/references/rust/02-crates-workspaces-and-layout.md +36 -0
  95. package/template/skills/engineering-standards-builder/references/rust/03-errors-ownership-and-concurrency.md +37 -0
  96. package/template/skills/engineering-standards-builder/references/rust/04-testing-tooling-quality-gates.md +44 -0
  97. package/template/skills/engineering-standards-builder/references/rust/README.md +9 -0
  98. package/template/skills/engineering-standards-builder/references/typescript/00-detection-and-scope.md +58 -0
  99. package/template/skills/engineering-standards-builder/references/typescript/01-language-and-type-system.md +49 -0
  100. package/template/skills/engineering-standards-builder/references/typescript/02-modules-packages-and-runtime-boundaries.md +49 -0
  101. package/template/skills/engineering-standards-builder/references/typescript/03-functions-async-errors-resources.md +42 -0
  102. package/template/skills/engineering-standards-builder/references/typescript/04-testing-tooling-quality-gates.md +51 -0
  103. package/template/skills/engineering-standards-builder/references/typescript/README.md +27 -0
  104. package/template/skills/engineering-standards-builder/references/typescript/app-types/cli.md +11 -0
  105. package/template/skills/engineering-standards-builder/references/typescript/app-types/library.md +11 -0
  106. package/template/skills/engineering-standards-builder/references/typescript/frameworks/react.md +71 -0
  107. package/template/skills/engineering-standards-builder/references/typescript/frameworks/vue.md +131 -0
  108. package/template/skills/engineering-standards-builder/references/typescript/runtimes/browser.md +15 -0
  109. package/template/skills/engineering-standards-builder/references/typescript/runtimes/electron.md +25 -0
  110. package/template/skills/engineering-standards-builder/references/typescript/runtimes/node.md +13 -0
  111. package/template/skills/engineering-standards-builder/scripts/discover-project.mjs +957 -0
  112. package/template/skills/engineering-standards-builder/scripts/self-test.mjs +203 -0
  113. package/template/skills/engineering-standards-builder/scripts/sync-manifest.mjs +105 -0
  114. package/template/skills/engineering-standards-builder/scripts/validate-builder.mjs +255 -0
  115. package/template/skills/engineering-standards-builder/scripts/validate-generated-skill.mjs +286 -0
  116. package/template/skills/engineering-standards-builder/templates/README.md +18 -0
  117. package/template/skills/engineering-standards-builder/templates/compatibility/agents-typescript--standards.md +6 -0
  118. package/template/skills/engineering-standards-builder/templates/compatibility/agents-typescript-standards.md +6 -0
  119. package/template/skills/engineering-standards-builder/templates/compatibility/claude-engineering-standards.md +6 -0
  120. package/template/skills/engineering-standards-builder/templates/compatibility/claude-typescript-standards.md +6 -0
  121. package/template/skills/engineering-standards-builder/templates/project-skill/SKILL.md.template +26 -0
  122. package/template/skills/engineering-standards-builder/templates/project-skill/references/project/00-project-profile.md.template +26 -0
  123. package/template/skills/engineering-standards-builder/templates/project-skill/references/project/01-module-map.md.template +27 -0
  124. package/template/skills/engineering-standards-builder/templates/project-skill/references/project/02-decisions-and-exceptions.md.template +31 -0
  125. package/template/skills/engineering-standards-builder/templates/project-skill/references/project/review-checklist.md.template +14 -0
  126. package/template/skills/migrate-runtime-state/references/migration-contract.md +3 -3
  127. package/template/skills/migrate-runtime-state/scripts/migrate-runtime-state.mjs +94 -12
  128. package/template/skills/source-code-zip-skill/SKILL.md +343 -0
  129. package/template/skills/source-code-zip-skill/scripts/zip_source_code.py +638 -0
  130. package/template/workflows/person/INDEX.md +1 -0
  131. package/template/workflows/person/S-steelman-deliberation/S-steelman-deliberation.md +218 -0
  132. package/template/workflows/person/S-steelman-deliberation/_templates/decision-template.md +51 -0
  133. package/template/workflows/person/S-steelman-deliberation/_templates/steelman-dossier-template.md +96 -0
  134. package/template/workflows/person/S-steelman-deliberation/deliberate.md +127 -0
  135. package/template/workflows/person/S-steelman-deliberation/evidence-gate.md +37 -0
  136. package/template/workflows/person/S-steelman-deliberation/judge.md +69 -0
  137. package/template/workflows/person/S-steelman-deliberation/tools/validate-steelman-change.mjs +697 -0
  138. package/template/workflows/specdev/I-implement/I-implement.md +27 -26
  139. package/template/workflows/specdev/I-implement/evidence-template.md +18 -12
  140. package/template/workflows/specdev/I-implement/execution-preflight.md +11 -9
  141. package/template/workflows/specdev/I-init-setup/I-init-setup.md +2 -2
  142. package/template/workflows/specdev/I-init-setup/change-status-template.json +3 -3
  143. package/template/workflows/specdev/I-init-setup/config-template.json +5 -2
  144. package/template/workflows/specdev/INDEX.md +6 -6
  145. package/template/workflows/specdev/P-goal-plan/P-goal-plan.md +16 -16
  146. package/template/workflows/specdev/P-goal-plan/completion-control.md +8 -8
  147. package/template/workflows/specdev/P-goal-plan/goal-plan-template.md +20 -14
  148. package/template/workflows/specdev/P-goal-plan/lead-orchestration.md +5 -5
  149. package/template/workflows/specdev/P-goal-plan/orchestration-protocol.md +13 -13
  150. package/template/workflows/specdev/P-goal-plan/planning-modes.md +27 -15
  151. package/template/workflows/specdev/P-prototype/ui-prototype.md +1 -1
  152. package/template/workflows/specdev/T-tickets/T-tickets.md +4 -4
  153. package/template/workflows/specdev/T-tickets/ticket-readiness.md +3 -3
  154. package/template/workflows/specdev/T-tickets/ticket-template.md +6 -6
  155. package/template/workflows/specdev/T-tickets/tickets-map-template.md +2 -2
  156. package/template/workflows/specdev/common/README.md +1 -1
  157. package/template/workflows/specdev/common/rules/change-completion.md +2 -2
  158. package/template/workflows/specdev/common/rules/evidence-and-verification.md +10 -6
  159. package/template/workflows/specdev/common/rules/path-ownership.md +4 -4
  160. package/template/workflows/specdev/common/schemas/change-status.schema.json +31 -4
  161. package/template/workflows/specdev/common/schemas/config.schema.json +12 -6
  162. package/template/workflows/specdev/common/schemas/goal-plan.schema.json +15 -6
  163. package/template/workflows/specdev/common/skills/dev-worktree/SKILL.md +3 -3
  164. package/template/workflows/specdev/common/skills/dev-worktree/references/finalize.md +1 -1
  165. package/template/workflows/specdev/common/skills/subagent-delivery/SKILL.md +4 -4
  166. package/template/workflows/specdev/common/skills/subagent-delivery/references/external-web-subagent.md +1 -1
  167. package/template/workflows/specdev/common/skills/subagent-delivery/references/native-subagent.md +4 -4
  168. package/template/workflows/specdev/common/tools/README.md +2 -1
  169. package/template/workflows/specdev/common/tools/validate-specdev.mjs +261 -66
  170. package/template/skills/typescript-standards-builder/README.md +0 -53
  171. package/template/skills/typescript-standards-builder/SKILL.md +0 -245
  172. package/template/skills/typescript-standards-builder/examples/sample-generated-tree.md +0 -30
  173. package/template/skills/typescript-standards-builder/examples/sample-interview-decisions.md +0 -26
  174. package/template/skills/typescript-standards-builder/manifest.txt +0 -29
  175. package/template/skills/typescript-standards-builder/references/00-governance-and-fixed-defaults.md +0 -77
  176. package/template/skills/typescript-standards-builder/references/01-project-discovery.md +0 -100
  177. package/template/skills/typescript-standards-builder/references/02-interview-workflow.md +0 -129
  178. package/template/skills/typescript-standards-builder/references/03-project-architecture-and-directory-layout.md +0 -84
  179. package/template/skills/typescript-standards-builder/references/04-file-directory-and-symbol-naming.md +0 -92
  180. package/template/skills/typescript-standards-builder/references/05-modules-imports-exports-and-dependencies.md +0 -63
  181. package/template/skills/typescript-standards-builder/references/06-typescript-type-system.md +0 -64
  182. package/template/skills/typescript-standards-builder/references/07-functions-async-errors-and-resources.md +0 -42
  183. package/template/skills/typescript-standards-builder/references/08-comments-jsdoc-and-documentation.md +0 -51
  184. package/template/skills/typescript-standards-builder/references/09-testing-strategy.md +0 -58
  185. package/template/skills/typescript-standards-builder/references/10-react-and-frontend.md +0 -39
  186. package/template/skills/typescript-standards-builder/references/11-node-cli-and-cross-platform.md +0 -31
  187. package/template/skills/typescript-standards-builder/references/12-formatting-lint-and-complexity.md +0 -58
  188. package/template/skills/typescript-standards-builder/references/13-configuration-dependencies-and-ci.md +0 -71
  189. package/template/skills/typescript-standards-builder/references/14-security-performance-and-i18n.md +0 -32
  190. package/template/skills/typescript-standards-builder/references/15-git-review-and-delivery.md +0 -28
  191. package/template/skills/typescript-standards-builder/references/16-adoption-exceptions-and-migration.md +0 -61
  192. package/template/skills/typescript-standards-builder/references/17-generation-contract.md +0 -104
  193. package/template/skills/typescript-standards-builder/references/README.md +0 -37
  194. package/template/skills/typescript-standards-builder/templates/agents-compat-skill/SKILL.md +0 -1
  195. package/template/skills/typescript-standards-builder/templates/claude-skill/SKILL.md +0 -1
  196. package/template/skills/typescript-standards-builder/templates/project-skill/SKILL.md.template +0 -34
  197. package/template/skills/typescript-standards-builder/templates/project-skill/references/00-project-profile.md.template +0 -17
  198. package/template/skills/typescript-standards-builder/templates/project-skill/references/10-review-checklist.md +0 -23
  199. package/template/skills/typescript-standards-builder/templates/project-skill/references/11-decisions-and-exceptions.md.template +0 -19
@@ -0,0 +1,638 @@
1
+ #!/usr/bin/env -S uv run --script
2
+ # /// script
3
+ # requires-python = ">=3.10"
4
+ # dependencies = []
5
+ # ///
6
+ """将源码目录打包为 ZIP,并通过正则规则排除依赖、构建产物、秘密文件和非代码文件。
7
+
8
+ 首选运行方式:
9
+ uv run zip_source_code.py /path/to/project
10
+
11
+ 本脚本仅使用 Python 标准库,不需要安装第三方依赖。
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import argparse
17
+ import os
18
+ import re
19
+ import sys
20
+ import tempfile
21
+ import zipfile
22
+ from dataclasses import dataclass
23
+ from pathlib import Path
24
+ from typing import Iterable, Pattern, Sequence
25
+
26
+ __version__ = "1.0.0"
27
+
28
+ # ---------------------------------------------------------------------------
29
+ # 可编辑配置
30
+ # ---------------------------------------------------------------------------
31
+ # IGNORE 是默认忽略字段。每一项都是正则表达式,匹配对象是:
32
+ # 1. 相对于待压缩目录的路径;
33
+ # 2. 路径分隔符统一为 "/";
34
+ # 3. 路径开头没有 "/";
35
+ # 4. 正则匹配默认不区分大小写,并使用 re.search。
36
+ #
37
+ # 示例:
38
+ # r"(^|/)node_modules(?:/|$)" -> 忽略任意层级的 node_modules 目录
39
+ # r"\.zip$" -> 忽略所有 .zip 文件
40
+ # r"(^|/)private/" -> 忽略任意层级名为 private 的目录
41
+ IGNORE: list[str] = [
42
+ # 版本控制、编辑器和系统元数据
43
+ r"(^|/)\.(?:git|hg|svn)(?:/|$)",
44
+ r"(^|/)(?:\.idea|\.vscode)(?:/|$)",
45
+ r"(^|/)(?:\.DS_Store|Thumbs\.db|desktop\.ini)$",
46
+
47
+ # 依赖目录、虚拟环境和缓存
48
+ r"(^|/)(?:node_modules?|bower_components|jspm_packages|vendor|Pods|Carthage)(?:/|$)",
49
+ r"(^|/)(?:\.venv|venv|env|__pycache__|\.pytest_cache|\.mypy_cache|\.ruff_cache|\.tox|\.nox|\.cache)(?:/|$)",
50
+
51
+ # 构建产物、覆盖率和框架缓存
52
+ r"(^|/)(?:dist|build|target|out|coverage|htmlcov|\.next|\.nuxt|\.svelte-kit|\.parcel-cache|\.turbo|\.gradle)(?:/|$)",
53
+ r"\.(?:min|bundle)\.(?:js|css)$",
54
+
55
+ # 环境变量、证书、密钥和常见凭据文件
56
+ r"(^|/)\.env(?:\..*)?$",
57
+ r"(^|/)(?:id_rsa|id_dsa|id_ecdsa|id_ed25519)(?:\.pub)?$",
58
+ r"\.(?:pem|key|p12|pfx|jks|keystore|crt|cer|der)$",
59
+
60
+ # 用户明确要求排除 YAML
61
+ r"\.ya?ml$",
62
+
63
+ # 压缩包、归档和编译产物
64
+ r"\.(?:zip|7z|rar|tar|tgz|tbz2?|txz|gz|bz2|xz|zst|jar|war|ear|whl|egg)$",
65
+ r"\.(?:exe|dll|so(?:\.\d+)*|dylib|bin|o|obj|a|lib|class|py[co]|pyd|wasm)$",
66
+
67
+ # 媒体、办公文档、数据库、日志和临时文件
68
+ r"\.(?:png|jpe?g|gif|webp|bmp|tiff?|ico|heic|avif|mp[34]|m4[av]|wav|flac|ogg|mov|avi|mkv|webm)$",
69
+ r"\.(?:pdf|docx?|xlsx?|pptx?|odt|ods|odp)$",
70
+ r"\.(?:sqlite3?|db|dump|log|tmp|temp|swp|swo|bak|old)$",
71
+
72
+ # 锁文件通常不是源代码;pnpm-lock.yaml 同时也会被 YAML 规则匹配
73
+ r"(^|/)(?:package-lock\.json|yarn\.lock|pnpm-lock\.ya?ml|poetry\.lock|uv\.lock|Pipfile\.lock|composer\.lock|Gemfile\.lock)$",
74
+ ]
75
+
76
+ # 默认“仅源码”模式允许的扩展名。扩展名统一使用小写并包含前导点。
77
+ CODE_EXTENSIONS: set[str] = {
78
+ # Python / JavaScript / TypeScript
79
+ ".py", ".pyi", ".pyx", ".pxd", ".pxi",
80
+ ".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx",
81
+
82
+ # Web / UI
83
+ ".html", ".htm", ".css", ".scss", ".sass", ".less",
84
+ ".vue", ".svelte", ".astro", ".mdx",
85
+
86
+ # JVM / Android
87
+ ".java", ".kt", ".kts", ".scala", ".groovy", ".gradle",
88
+
89
+ # Native / systems
90
+ ".c", ".h", ".cc", ".cpp", ".cxx", ".hpp", ".hxx",
91
+ ".m", ".mm", ".swift", ".rs", ".go", ".zig", ".nim",
92
+ ".asm", ".s", ".v", ".sv", ".svh", ".vhd", ".vhdl",
93
+
94
+ # .NET
95
+ ".cs", ".fs", ".fsx", ".fsi", ".vb",
96
+
97
+ # 脚本和命令行
98
+ ".sh", ".bash", ".zsh", ".fish", ".ps1", ".bat", ".cmd",
99
+ ".rb", ".php", ".pl", ".pm", ".lua", ".tcl",
100
+
101
+ # 函数式语言和其他语言
102
+ ".ex", ".exs", ".erl", ".hrl", ".clj", ".cljs", ".cljc",
103
+ ".hs", ".lhs", ".ml", ".mli", ".r", ".jl", ".cr",
104
+ ".dart", ".sol", ".move",
105
+
106
+ # 数据库、接口描述和基础设施即代码
107
+ ".sql", ".proto", ".graphql", ".gql", ".thrift",
108
+ ".tf", ".hcl", ".nix",
109
+
110
+ # 构建脚本和模板源码
111
+ ".cmake", ".mk", ".mak", ".mustache", ".hbs", ".ejs",
112
+ ".jinja", ".jinja2", ".j2", ".twig", ".tex",
113
+ }
114
+
115
+ # 无扩展名或特殊命名的常见源码/构建脚本。比较时不区分大小写。
116
+ CODE_FILENAMES: set[str] = {
117
+ "dockerfile",
118
+ "containerfile",
119
+ "makefile",
120
+ "gnumakefile",
121
+ "cmakelists.txt",
122
+ "jenkinsfile",
123
+ "vagrantfile",
124
+ "rakefile",
125
+ "gemfile",
126
+ "procfile",
127
+ "justfile",
128
+ "meson.build",
129
+ "meson_options.txt",
130
+ "build",
131
+ "workspace",
132
+ "build.bazel",
133
+ "workspace.bazel",
134
+ "module.bazel",
135
+ }
136
+
137
+
138
+ class ZipSourceError(Exception):
139
+ """可展示给终端用户的错误。"""
140
+
141
+
142
+ @dataclass(frozen=True)
143
+ class Candidate:
144
+ absolute_path: Path
145
+ relative_path: Path
146
+ size: int
147
+
148
+
149
+ @dataclass
150
+ class ScanStats:
151
+ included: int = 0
152
+ included_bytes: int = 0
153
+ ignored: int = 0
154
+ non_code: int = 0
155
+ symlinks: int = 0
156
+ output_file: int = 0
157
+
158
+
159
+ @dataclass(frozen=True)
160
+ class RegexRule:
161
+ source: str
162
+ compiled: Pattern[str]
163
+
164
+
165
+ def human_size(size: int) -> str:
166
+ """把字节数格式化为易读字符串。"""
167
+ units = ("B", "KiB", "MiB", "GiB", "TiB")
168
+ value = float(size)
169
+ for unit in units:
170
+ if value < 1024.0 or unit == units[-1]:
171
+ if unit == "B":
172
+ return f"{int(value)} {unit}"
173
+ return f"{value:.2f} {unit}"
174
+ value /= 1024.0
175
+ return f"{size} B"
176
+
177
+
178
+ def normalized_relative(path: Path) -> str:
179
+ """返回用于正则匹配的 POSIX 风格相对路径。"""
180
+ return path.as_posix()
181
+
182
+
183
+ def compile_rules(patterns: Iterable[str], label: str) -> list[RegexRule]:
184
+ rules: list[RegexRule] = []
185
+ for pattern in patterns:
186
+ try:
187
+ compiled = re.compile(pattern, re.IGNORECASE)
188
+ except re.error as exc:
189
+ raise ZipSourceError(f"{label} 中存在无效正则 {pattern!r}: {exc}") from exc
190
+ rules.append(RegexRule(source=pattern, compiled=compiled))
191
+ return rules
192
+
193
+
194
+ def first_match(rules: Sequence[RegexRule], relative_path: str) -> RegexRule | None:
195
+ for rule in rules:
196
+ if rule.compiled.search(relative_path):
197
+ return rule
198
+ return None
199
+
200
+
201
+ def load_regex_file(path: Path) -> list[str]:
202
+ """读取“每行一个正则”的忽略文件;空行和以 # 开头的行会被忽略。"""
203
+ try:
204
+ text = path.read_text(encoding="utf-8")
205
+ except OSError as exc:
206
+ raise ZipSourceError(f"无法读取忽略规则文件 {path}: {exc}") from exc
207
+
208
+ patterns: list[str] = []
209
+ for line_number, raw_line in enumerate(text.splitlines(), start=1):
210
+ line = raw_line.strip()
211
+ if not line or line.startswith("#"):
212
+ continue
213
+ try:
214
+ re.compile(line, re.IGNORECASE)
215
+ except re.error as exc:
216
+ raise ZipSourceError(
217
+ f"忽略规则文件 {path} 第 {line_number} 行正则无效: {exc}"
218
+ ) from exc
219
+ patterns.append(line)
220
+ return patterns
221
+
222
+
223
+ def has_shebang(path: Path) -> bool:
224
+ """无扩展名文件若以 shebang 开头,也视为源码脚本。"""
225
+ if path.suffix:
226
+ return False
227
+ try:
228
+ with path.open("rb") as file_obj:
229
+ return file_obj.read(2) == b"#!"
230
+ except OSError:
231
+ # 后续 stat/压缩阶段会给出更明确的错误;这里仅返回非源码。
232
+ return False
233
+
234
+
235
+ def is_code_file(path: Path) -> bool:
236
+ name = path.name.lower()
237
+ if name in CODE_FILENAMES:
238
+ return True
239
+ if path.suffix.lower() in CODE_EXTENSIONS:
240
+ return True
241
+ return has_shebang(path)
242
+
243
+
244
+ def archive_root_name(source: Path) -> str:
245
+ """生成 ZIP 内部的顶层目录名。"""
246
+ if source.name:
247
+ return source.name
248
+ anchor = source.anchor.replace(":", "").strip("/\\")
249
+ return anchor or "root"
250
+
251
+
252
+ def default_output_path(source: Path) -> Path:
253
+ name = archive_root_name(source)
254
+ return source.parent / f"{name}.code.zip"
255
+
256
+
257
+ def normalize_output_path(path: Path) -> Path:
258
+ expanded = path.expanduser()
259
+ if expanded.suffix.lower() != ".zip":
260
+ expanded = expanded.with_name(expanded.name + ".zip")
261
+ return expanded.resolve(strict=False)
262
+
263
+
264
+ def log_skip(verbose: bool, relative_path: str, reason: str) -> None:
265
+ if verbose:
266
+ print(f"[跳过:{reason}] {relative_path}")
267
+
268
+
269
+ def scan_source(
270
+ source: Path,
271
+ output: Path,
272
+ ignore_rules: Sequence[RegexRule],
273
+ include_rules: Sequence[RegexRule],
274
+ code_only: bool,
275
+ verbose: bool,
276
+ ) -> tuple[list[Candidate], ScanStats]:
277
+ """扫描目录并返回待写入 ZIP 的文件列表。"""
278
+ candidates: list[Candidate] = []
279
+ stats = ScanStats()
280
+
281
+ output_resolved = output.resolve(strict=False)
282
+
283
+ def on_walk_error(error: OSError) -> None:
284
+ raise error
285
+
286
+ try:
287
+ walker = os.walk(source, topdown=True, followlinks=False, onerror=on_walk_error)
288
+ for root_text, directory_names, file_names in walker:
289
+ root = Path(root_text)
290
+
291
+ # 排序确保不同机器上的归档条目顺序尽量稳定。
292
+ directory_names.sort()
293
+ file_names.sort()
294
+
295
+ kept_directories: list[str] = []
296
+ for directory_name in directory_names:
297
+ absolute_dir = root / directory_name
298
+ relative_dir = absolute_dir.relative_to(source)
299
+ relative_text = normalized_relative(relative_dir)
300
+
301
+ if absolute_dir.is_symlink():
302
+ stats.symlinks += 1
303
+ log_skip(verbose, relative_text, "符号链接目录")
304
+ continue
305
+
306
+ matched = first_match(ignore_rules, relative_text)
307
+ if matched is not None:
308
+ stats.ignored += 1
309
+ log_skip(verbose, relative_text, f"正则 {matched.source}")
310
+ continue
311
+
312
+ kept_directories.append(directory_name)
313
+
314
+ # 原地修改,阻止 os.walk 进入已忽略目录。
315
+ directory_names[:] = kept_directories
316
+
317
+ for file_name in file_names:
318
+ absolute_file = root / file_name
319
+ relative_file = absolute_file.relative_to(source)
320
+ relative_text = normalized_relative(relative_file)
321
+
322
+ if absolute_file.is_symlink():
323
+ stats.symlinks += 1
324
+ log_skip(verbose, relative_text, "符号链接文件")
325
+ continue
326
+
327
+ if absolute_file.resolve(strict=False) == output_resolved:
328
+ stats.output_file += 1
329
+ log_skip(verbose, relative_text, "输出 ZIP 本身")
330
+ continue
331
+
332
+ matched = first_match(ignore_rules, relative_text)
333
+ if matched is not None:
334
+ stats.ignored += 1
335
+ log_skip(verbose, relative_text, f"正则 {matched.source}")
336
+ continue
337
+
338
+ explicitly_included = first_match(include_rules, relative_text) is not None
339
+ if code_only and not explicitly_included and not is_code_file(absolute_file):
340
+ stats.non_code += 1
341
+ log_skip(verbose, relative_text, "非源码文件")
342
+ continue
343
+
344
+ try:
345
+ file_size = absolute_file.stat().st_size
346
+ except OSError as exc:
347
+ raise ZipSourceError(f"无法读取文件信息 {absolute_file}: {exc}") from exc
348
+
349
+ candidates.append(
350
+ Candidate(
351
+ absolute_path=absolute_file,
352
+ relative_path=relative_file,
353
+ size=file_size,
354
+ )
355
+ )
356
+ stats.included += 1
357
+ stats.included_bytes += file_size
358
+ except OSError as exc:
359
+ raise ZipSourceError(f"扫描目录失败: {exc}") from exc
360
+
361
+ candidates.sort(key=lambda item: item.relative_path.as_posix())
362
+ return candidates, stats
363
+
364
+
365
+ def create_archive(
366
+ source: Path,
367
+ output: Path,
368
+ candidates: Sequence[Candidate],
369
+ compression_level: int,
370
+ contents_only: bool,
371
+ ) -> None:
372
+ """原子方式创建 ZIP:成功后才替换最终输出文件。"""
373
+ try:
374
+ output.parent.mkdir(parents=True, exist_ok=True)
375
+ except OSError as exc:
376
+ raise ZipSourceError(f"无法创建输出目录 {output.parent}: {exc}") from exc
377
+
378
+ temp_path: Path | None = None
379
+ try:
380
+ file_descriptor, temp_name = tempfile.mkstemp(
381
+ prefix=f".{output.name}.",
382
+ suffix=".tmp",
383
+ dir=output.parent,
384
+ )
385
+ os.close(file_descriptor)
386
+ temp_path = Path(temp_name)
387
+
388
+ compression = zipfile.ZIP_STORED if compression_level == 0 else zipfile.ZIP_DEFLATED
389
+ zip_kwargs: dict[str, object] = {
390
+ "mode": "w",
391
+ "compression": compression,
392
+ "allowZip64": True,
393
+ }
394
+ if compression != zipfile.ZIP_STORED:
395
+ zip_kwargs["compresslevel"] = compression_level
396
+
397
+ root_name = archive_root_name(source)
398
+ with zipfile.ZipFile(temp_path, **zip_kwargs) as archive:
399
+ for candidate in candidates:
400
+ relative_text = candidate.relative_path.as_posix()
401
+ archive_name = relative_text if contents_only else f"{root_name}/{relative_text}"
402
+ archive.write(candidate.absolute_path, arcname=archive_name)
403
+
404
+ os.replace(temp_path, output)
405
+ temp_path = None
406
+ except (OSError, RuntimeError, zipfile.BadZipFile) as exc:
407
+ raise ZipSourceError(f"创建 ZIP 失败: {exc}") from exc
408
+ finally:
409
+ if temp_path is not None:
410
+ try:
411
+ temp_path.unlink(missing_ok=True)
412
+ except OSError:
413
+ pass
414
+
415
+
416
+ def print_defaults() -> None:
417
+ print("默认 IGNORE 正则:")
418
+ for pattern in IGNORE:
419
+ print(f" {pattern}")
420
+ print("\n默认源码扩展名:")
421
+ print(" " + " ".join(sorted(CODE_EXTENSIONS)))
422
+ print("\n默认特殊源码文件名:")
423
+ print(" " + " ".join(sorted(CODE_FILENAMES)))
424
+
425
+
426
+ def build_parser() -> argparse.ArgumentParser:
427
+ parser = argparse.ArgumentParser(
428
+ description="将指定目录压缩为只包含源码的 ZIP。默认使用正则排除依赖、构建产物、秘密文件、YAML 和压缩包。",
429
+ formatter_class=argparse.RawDescriptionHelpFormatter,
430
+ epilog=(
431
+ "示例:\n"
432
+ " uv run zip_source_code.py ./my-project\n"
433
+ " uv run zip_source_code.py ./my-project -o ./backup/project.zip --force\n"
434
+ " uv run zip_source_code.py ./my-project --dry-run --verbose\n"
435
+ " uv run zip_source_code.py ./my-project --ignore '(^|/)fixtures?(/|$)'\n"
436
+ " uv run zip_source_code.py ./my-project --include '(^|/)package\\.json$'\n"
437
+ "\n"
438
+ "注意:--ignore 和 IGNORE 使用正则表达式,不是 Git 的 glob 语法。"
439
+ ),
440
+ )
441
+ parser.add_argument(
442
+ "directory",
443
+ nargs="?",
444
+ type=Path,
445
+ help="要压缩的源码目录",
446
+ )
447
+ parser.add_argument(
448
+ "-o",
449
+ "--output",
450
+ type=Path,
451
+ help="输出 ZIP;默认是待压缩目录旁边的 <目录名>.code.zip",
452
+ )
453
+ parser.add_argument(
454
+ "-f",
455
+ "--force",
456
+ action="store_true",
457
+ help="允许覆盖已存在的输出 ZIP",
458
+ )
459
+ parser.add_argument(
460
+ "--ignore",
461
+ action="append",
462
+ default=[],
463
+ metavar="REGEX",
464
+ help="追加忽略正则;可重复使用",
465
+ )
466
+ parser.add_argument(
467
+ "--ignore-from",
468
+ action="append",
469
+ default=[],
470
+ type=Path,
471
+ metavar="FILE",
472
+ help="从 UTF-8 文件读取忽略正则,每行一个;空行和 # 注释会被忽略;可重复使用",
473
+ )
474
+ parser.add_argument(
475
+ "--include",
476
+ action="append",
477
+ default=[],
478
+ metavar="REGEX",
479
+ help="在仅源码模式下额外纳入匹配文件;默认 IGNORE 仍优先;可重复使用",
480
+ )
481
+ parser.add_argument(
482
+ "--all-files",
483
+ action="store_true",
484
+ help="关闭源码扩展名白名单,纳入所有未被 IGNORE 排除的普通文件",
485
+ )
486
+ parser.add_argument(
487
+ "--no-default-ignore",
488
+ action="store_true",
489
+ help="禁用脚本内置 IGNORE;可能把秘密文件或依赖打包,请谨慎使用",
490
+ )
491
+ parser.add_argument(
492
+ "--contents-only",
493
+ action="store_true",
494
+ help="ZIP 内不创建顶层目录,直接写入目录内容",
495
+ )
496
+ parser.add_argument(
497
+ "--compression-level",
498
+ type=int,
499
+ choices=range(0, 10),
500
+ default=9,
501
+ metavar="0-9",
502
+ help="压缩级别,0 表示仅存储,1 最快,9 压缩率最高;默认 9",
503
+ )
504
+ parser.add_argument(
505
+ "--dry-run",
506
+ action="store_true",
507
+ help="只显示将被纳入的文件,不创建 ZIP",
508
+ )
509
+ parser.add_argument(
510
+ "--allow-empty",
511
+ action="store_true",
512
+ help="即使没有匹配文件,也允许创建空 ZIP",
513
+ )
514
+ output_group = parser.add_mutually_exclusive_group()
515
+ output_group.add_argument(
516
+ "-v",
517
+ "--verbose",
518
+ action="store_true",
519
+ help="显示每个被跳过文件的原因",
520
+ )
521
+ output_group.add_argument(
522
+ "-q",
523
+ "--quiet",
524
+ action="store_true",
525
+ help="成功时仅输出 ZIP 的绝对路径",
526
+ )
527
+ parser.add_argument(
528
+ "--show-defaults",
529
+ action="store_true",
530
+ help="显示内置 IGNORE、源码扩展名和特殊文件名后退出",
531
+ )
532
+ parser.add_argument(
533
+ "--version",
534
+ action="version",
535
+ version=f"%(prog)s {__version__}",
536
+ )
537
+ return parser
538
+
539
+
540
+ def run(argv: Sequence[str] | None = None) -> int:
541
+ parser = build_parser()
542
+ args = parser.parse_args(argv)
543
+
544
+ if args.show_defaults:
545
+ print_defaults()
546
+ return 0
547
+
548
+ if args.directory is None:
549
+ parser.error("缺少要压缩的目录")
550
+
551
+ source = args.directory.expanduser().resolve(strict=False)
552
+ if not source.exists():
553
+ raise ZipSourceError(f"目录不存在: {source}")
554
+ if not source.is_dir():
555
+ raise ZipSourceError(f"参数不是目录: {source}")
556
+
557
+ output = normalize_output_path(args.output) if args.output else default_output_path(source)
558
+ output = output.resolve(strict=False)
559
+
560
+ if output.exists() and output.is_dir():
561
+ raise ZipSourceError(f"输出路径是目录,不是 ZIP 文件: {output}")
562
+ if output.exists() and not args.force and not args.dry_run:
563
+ raise ZipSourceError(f"输出文件已存在: {output};使用 --force 覆盖")
564
+
565
+ ignore_patterns: list[str] = [] if args.no_default_ignore else list(IGNORE)
566
+ ignore_patterns.extend(args.ignore)
567
+ for regex_file in args.ignore_from:
568
+ ignore_patterns.extend(load_regex_file(regex_file.expanduser().resolve(strict=False)))
569
+
570
+ ignore_rules = compile_rules(ignore_patterns, "忽略规则")
571
+ include_rules = compile_rules(args.include, "额外纳入规则")
572
+
573
+ candidates, stats = scan_source(
574
+ source=source,
575
+ output=output,
576
+ ignore_rules=ignore_rules,
577
+ include_rules=include_rules,
578
+ code_only=not args.all_files,
579
+ verbose=args.verbose,
580
+ )
581
+
582
+ if args.dry_run and not args.quiet:
583
+ for candidate in candidates:
584
+ print(f"[包含] {candidate.relative_path.as_posix()}")
585
+
586
+ if not candidates and not args.allow_empty:
587
+ raise ZipSourceError(
588
+ "没有找到可打包文件;可检查 IGNORE/源码扩展名,或使用 --all-files、--include、--allow-empty"
589
+ )
590
+
591
+ if not args.quiet:
592
+ print(
593
+ "扫描结果: "
594
+ f"包含 {stats.included} 个文件({human_size(stats.included_bytes)});"
595
+ f"正则忽略 {stats.ignored} 项;"
596
+ f"非源码 {stats.non_code} 项;"
597
+ f"符号链接 {stats.symlinks} 项。"
598
+ )
599
+
600
+ if args.dry_run:
601
+ if not args.quiet:
602
+ print("预览完成:未创建 ZIP。")
603
+ return 0
604
+
605
+ create_archive(
606
+ source=source,
607
+ output=output,
608
+ candidates=candidates,
609
+ compression_level=args.compression_level,
610
+ contents_only=args.contents_only,
611
+ )
612
+
613
+ try:
614
+ archive_size = output.stat().st_size
615
+ except OSError:
616
+ archive_size = 0
617
+
618
+ if args.quiet:
619
+ print(output)
620
+ else:
621
+ print(f"已创建: {output}")
622
+ print(f"ZIP 大小: {human_size(archive_size)}")
623
+ return 0
624
+
625
+
626
+ def main() -> None:
627
+ try:
628
+ raise SystemExit(run())
629
+ except ZipSourceError as exc:
630
+ print(f"错误: {exc}", file=sys.stderr)
631
+ raise SystemExit(1) from exc
632
+ except KeyboardInterrupt:
633
+ print("\n操作已取消。", file=sys.stderr)
634
+ raise SystemExit(130)
635
+
636
+
637
+ if __name__ == "__main__":
638
+ main()
@@ -10,3 +10,4 @@ auto_generated: true
10
10
  > 本文件由 `generate-index.mjs` 自动生成,**禁止手动编辑**。
11
11
 
12
12
  - **M-mao-zedong-cognitive-os** — 毛泽东认知操作系统:以毛泽东方法论为底座的问题诊断、战略制定与行动规划咨询
13
+ - **S-steelman-deliberation** — 双向钢人论证:将尚未厘清的复杂问题经真实问题重述、正反双向钢人化与关键变量识别,在一次关键追问后推进为明确判断和可执行行动