@winspan/claude-forge 8.53.2 → 8.54.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DEVELOPMENT.md +290 -221
- package/README.md +50 -8
- package/dist/cli/commands/skills.d.ts.map +1 -1
- package/dist/cli/commands/skills.js +7 -3
- package/dist/cli/commands/skills.js.map +1 -1
- package/dist/cli/init/hook-manager.d.ts +1 -1
- package/dist/cli/init/hook-manager.d.ts.map +1 -1
- package/dist/cli/init/hook-manager.js +1 -0
- package/dist/cli/init/hook-manager.js.map +1 -1
- package/dist/core/storage/events.d.ts.map +1 -1
- package/dist/core/storage/events.js +0 -1
- package/dist/core/storage/events.js.map +1 -1
- package/dist/core/storage/maintenance.d.ts +25 -3
- package/dist/core/storage/maintenance.d.ts.map +1 -1
- package/dist/core/storage/maintenance.js +33 -4
- package/dist/core/storage/maintenance.js.map +1 -1
- package/dist/core/storage/routing.d.ts +4 -0
- package/dist/core/storage/routing.d.ts.map +1 -1
- package/dist/core/storage/routing.js +10 -4
- package/dist/core/storage/routing.js.map +1 -1
- package/dist/core/storage/sessions.d.ts +17 -0
- package/dist/core/storage/sessions.d.ts.map +1 -1
- package/dist/core/storage/sessions.js +64 -0
- package/dist/core/storage/sessions.js.map +1 -1
- package/dist/core/storage/skills.d.ts +4 -0
- package/dist/core/storage/skills.d.ts.map +1 -1
- package/dist/core/storage/skills.js +10 -2
- package/dist/core/storage/skills.js.map +1 -1
- package/dist/core/storage/sqlite.d.ts +5 -0
- package/dist/core/storage/sqlite.d.ts.map +1 -1
- package/dist/core/storage/sqlite.js +6 -0
- package/dist/core/storage/sqlite.js.map +1 -1
- package/dist/core/storage/tasks.d.ts.map +1 -1
- package/dist/core/storage/tasks.js +2 -0
- package/dist/core/storage/tasks.js.map +1 -1
- package/dist/core/types.d.ts +7 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +19 -4
- package/dist/daemon/index.js.map +1 -1
- package/dist/skills/registry.d.ts.map +1 -1
- package/dist/skills/registry.js +13 -2
- package/dist/skills/registry.js.map +1 -1
- package/dist/skills/semantic-matcher.d.ts +2 -2
- package/dist/skills/semantic-matcher.d.ts.map +1 -1
- package/dist/skills/semantic-matcher.js +14 -19
- package/dist/skills/semantic-matcher.js.map +1 -1
- package/dist/skills/upgrade-engine.d.ts +3 -1
- package/dist/skills/upgrade-engine.d.ts.map +1 -1
- package/dist/skills/upgrade-engine.js +25 -14
- package/dist/skills/upgrade-engine.js.map +1 -1
- package/dist/web/analytics/weekly-report.d.ts.map +1 -1
- package/dist/web/analytics/weekly-report.js +21 -29
- package/dist/web/analytics/weekly-report.js.map +1 -1
- package/dist/web/routes/patch.d.ts.map +1 -1
- package/dist/web/routes/patch.js +32 -2
- package/dist/web/routes/patch.js.map +1 -1
- package/dist/web/routes/sessions.d.ts.map +1 -1
- package/dist/web/routes/sessions.js +9 -7
- package/dist/web/routes/sessions.js.map +1 -1
- package/dist/web/routes/trace.d.ts.map +1 -1
- package/dist/web/routes/trace.js +2 -3
- package/dist/web/routes/trace.js.map +1 -1
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +3 -2
- package/dist/web/server.js.map +1 -1
- package/package.json +12 -2
- package/scripts/postinstall.cjs +21 -0
- package/.claude/CLAUDE.md +0 -17
- package/.eslintrc.js +0 -23
- package/.prettierrc +0 -8
- package/ARCHITECTURE_ISSUES.md +0 -249
- package/CLAUDE.md +0 -265
- package/CLAUDE.md.backup +0 -488
- package/docs/concurrent-agents.md +0 -129
- package/docs/design/architecture-review-20260516.md +0 -232
- package/docs/design/fix-skills-data-and-set-leak-spec-20260516-1300.md +0 -219
- package/docs/design/h1-storage-aggregation-spec-20260518-1121.md +0 -299
- package/docs/design/h2-getdatabase-encapsulation-spec-20260518-1450.md +0 -191
- package/docs/design/h3-fallback-removal-spec-20260518-1245.md +0 -76
- package/docs/design/h4-index-dedup-spec-20260518-1230.md +0 -109
- package/docs/design/h6-services-migration-spec-20260518-1355.md +0 -82
- package/docs/design/hook-failure-queue-spec-20260516-1530.md +0 -204
- package/docs/design/l1-swarm-protocol-extract-spec-20260518-1605.md +0 -106
- package/docs/design/m10-forge-paths-spec-20260518-1320.md +0 -121
- package/docs/design/m2-m3-tool-input-spec-20260518-1425.md +0 -131
- package/docs/design/m7-routing-event-association-spec-20260518-1545.md +0 -103
- package/docs/design/project-path-gitroot-spec-20260518-1715.md +0 -134
- package/docs/design/refactor-phase1-spec-20260515-1600.md +0 -543
- package/docs/design/refactor-phase2-spec-20260515-1700.md +0 -424
- package/docs/design/skill-ai-upgrade-spec-20260518-1930.md +0 -297
- package/docs/design/task-active-gc-spec-20260518-1745.md +0 -146
- package/docs/design/tasks-list-filter-pagination-spec-20260518-0930.md +0 -208
- package/docs/implementation/daemon-skill-sync-changelog-20260518-2000.md +0 -22
- package/docs/implementation/fix-skills-data-and-set-leak-changelog-20260516-1300.md +0 -104
- package/docs/implementation/h1-storage-aggregation-changelog-20260518-1121.md +0 -82
- package/docs/implementation/h2-final-changelog-20260518-1530.md +0 -61
- package/docs/implementation/h2-phase1-safety-net-changelog-20260518-1450.md +0 -70
- package/docs/implementation/h2-phase2-operations-changelog-20260518-1450.md +0 -120
- package/docs/implementation/h2-phase3-callsites-changelog-20260518-1450.md +0 -71
- package/docs/implementation/h3-fallback-removal-changelog-20260518-1245.md +0 -71
- package/docs/implementation/h4-index-dedup-changelog-20260518-1230.md +0 -60
- package/docs/implementation/h6-services-migration-changelog-20260518-1355.md +0 -46
- package/docs/implementation/h7-m9-defaults-changelog-20260518-1300.md +0 -46
- package/docs/implementation/hook-failure-queue-changelog-20260516-1530.md +0 -196
- package/docs/implementation/hotfix-daemon-event-reject-20260516-1430.md +0 -56
- package/docs/implementation/l1-swarm-protocol-extract-changelog-20260518-1605.md +0 -45
- package/docs/implementation/l3-l4-daemon-perf-changelog-20260518-1410.md +0 -63
- package/docs/implementation/l6-l8-final-cleanup-changelog-20260518-1640.md +0 -38
- package/docs/implementation/m1-m4-m5-l7-cleanup-changelog-20260518-1310.md +0 -58
- package/docs/implementation/m10-forge-paths-changelog-20260518-1320.md +0 -60
- package/docs/implementation/m2-m3-tool-input-changelog-20260518-1425.md +0 -43
- package/docs/implementation/m6-m8-naming-shutdown-changelog-20260518-1340.md +0 -56
- package/docs/implementation/m7-routing-association-changelog-20260518-1545.md +0 -69
- package/docs/implementation/project-path-gitroot-changelog-20260518-1715.md +0 -63
- package/docs/implementation/refactor-phase1-changelog-20260515-1630.md +0 -354
- package/docs/implementation/refactor-phase2-changelog-20260515-1705.md +0 -421
- package/docs/implementation/skill-ai-upgrade-changelog-20260518-1930.md +0 -49
- package/docs/implementation/task-active-gc-changelog-20260518-1745.md +0 -35
- package/docs/implementation/task-title-summary-changelog-20260518-1130.md +0 -39
- package/docs/implementation/tasks-detail-back-loses-filters-changelog-20260518-1100.md +0 -22
- package/docs/implementation/tasks-list-filter-pagination-changelog-20260518-0930.md +0 -72
- package/docs/implementation/tasks-page-white-screen-hotfix-changelog-20260518-1015.md +0 -56
- package/docs/reviews/claudemd-template-sync.md +0 -54
- package/docs/reviews/task-title-summary.md +0 -92
- package/docs/reviews/tasks-detail-back-loses-filters.md +0 -58
- package/docs/reviews/tasks-filter-pagination.md +0 -80
- package/docs/reviews/tasks-page-white-screen-hotfix.md +0 -126
- package/docs/ruflo-learning-strategy.md +0 -322
- package/docs/skills-deduplication-analysis.md +0 -83
- package/docs/skills-multiformat-support.md +0 -177
- package/docs/skills-third-party.md +0 -183
- package/docs/testing/tasks-filter-pagination-test-report.md +0 -86
- package/forge +0 -321
- package/playwright.config.ts +0 -40
- package/scripts/demo-v2.ts +0 -91
- package/scripts/dev-daemon.sh +0 -232
- package/scripts/dev-web.ts +0 -109
- package/scripts/e2e-mcp-link.ts +0 -423
- package/scripts/e2e-methodology-quality.ts +0 -253
- package/scripts/e2e-routing.ts +0 -456
- package/scripts/e2e-user-methodology.ts +0 -326
- package/scripts/e2e-web-workflows.ts +0 -299
- package/scripts/migrate-legacy-to-dynamic.sql +0 -108
- package/scripts/regenerate-execution-docs.ts +0 -116
- package/scripts/sync-agent-skills.ts +0 -193
- package/scripts/test-hook.sh +0 -71
- package/scripts/verify-skill-loading.ts +0 -62
- package/src/claudemd/claudemd-generator.ts +0 -568
- package/src/claudemd/convention-extractor.ts +0 -69
- package/src/claudemd/index.ts +0 -35
- package/src/claudemd/persona-manager.ts +0 -88
- package/src/claudemd/resume-manager.ts +0 -236
- package/src/claudemd/tech-detector.ts +0 -220
- package/src/claudemd/templates/swarm-protocol.md +0 -222
- package/src/cli/commands/claudemd.ts +0 -84
- package/src/cli/commands/config.ts +0 -46
- package/src/cli/commands/daemon.ts +0 -310
- package/src/cli/commands/executions.ts +0 -115
- package/src/cli/commands/init.ts +0 -204
- package/src/cli/commands/logs.ts +0 -181
- package/src/cli/commands/mcp.ts +0 -242
- package/src/cli/commands/menu.ts +0 -357
- package/src/cli/commands/skills.ts +0 -328
- package/src/cli/commands/stats.ts +0 -73
- package/src/cli/commands/status.ts +0 -69
- package/src/cli/commands/template.ts +0 -77
- package/src/cli/commands/trace.ts +0 -148
- package/src/cli/index.ts +0 -42
- package/src/cli/init/hook-manager.ts +0 -132
- package/src/core/ai/provider.ts +0 -308
- package/src/core/ai/types.ts +0 -51
- package/src/core/config.ts +0 -124
- package/src/core/constants.ts +0 -67
- package/src/core/event-fields.ts +0 -32
- package/src/core/queue/index.ts +0 -192
- package/src/core/storage/base.ts +0 -302
- package/src/core/storage/events.ts +0 -434
- package/src/core/storage/injections.ts +0 -78
- package/src/core/storage/maintenance.ts +0 -59
- package/src/core/storage/migrations/002_add_skill_tracking.sql +0 -6
- package/src/core/storage/migrations/003_add_skill_invocations.sql +0 -23
- package/src/core/storage/performance-indexes.sql +0 -23
- package/src/core/storage/routing.ts +0 -322
- package/src/core/storage/rows.ts +0 -112
- package/src/core/storage/schema.sql +0 -224
- package/src/core/storage/sessions.ts +0 -168
- package/src/core/storage/skills.ts +0 -233
- package/src/core/storage/sqlite.ts +0 -293
- package/src/core/storage/tasks.ts +0 -318
- package/src/core/storage/token-usage.ts +0 -93
- package/src/core/types.ts +0 -181
- package/src/core/utils/error-handler.ts +0 -257
- package/src/core/utils/forge-resume-block.ts +0 -74
- package/src/core/utils/format.ts +0 -69
- package/src/core/utils/git.ts +0 -23
- package/src/core/utils/logger.ts +0 -134
- package/src/core/utils/lru-cache.ts +0 -54
- package/src/core/utils/path.ts +0 -19
- package/src/core/utils/session.ts +0 -26
- package/src/core/utils/time.ts +0 -37
- package/src/core/utils/token-tracker.ts +0 -97
- package/src/daemon/event-parser.ts +0 -36
- package/src/daemon/handlers/history-exporter.ts +0 -117
- package/src/daemon/handlers/post-tool-use.ts +0 -54
- package/src/daemon/handlers/stop.ts +0 -208
- package/src/daemon/handlers/user-prompt.ts +0 -178
- package/src/daemon/hook-sync.ts +0 -91
- package/src/daemon/index.ts +0 -312
- package/src/daemon/launchd/com.claude-forge.daemon.plist.template +0 -47
- package/src/daemon/launchd-installer.ts +0 -260
- package/src/daemon/lifecycle.ts +0 -128
- package/src/daemon/router.ts +0 -40
- package/src/daemon/server.ts +0 -196
- package/src/daemon/services/task-segmenter.ts +0 -112
- package/src/daemon/skill-sync.ts +0 -88
- package/src/hooks/hook-lib.sh +0 -118
- package/src/hooks/notification.sh +0 -35
- package/src/hooks/post-tool-use.sh +0 -61
- package/src/hooks/pre-tool-use.sh +0 -63
- package/src/hooks/stop.sh +0 -43
- package/src/hooks/user-prompt-submit.sh +0 -69
- package/src/mcp/server.ts +0 -322
- package/src/skills/index.ts +0 -2
- package/src/skills/invocation-guard.ts +0 -177
- package/src/skills/matcher.ts +0 -148
- package/src/skills/official/code-simplifier.md +0 -52
- package/src/skills/official/find-skills.md +0 -142
- package/src/skills/official/official-api-design.md +0 -30
- package/src/skills/official/official-architecture-decision.md +0 -41
- package/src/skills/official/official-bmad.md +0 -118
- package/src/skills/official/official-db-schema-design.md +0 -34
- package/src/skills/official/official-debug.md +0 -25
- package/src/skills/official/official-doc-driven.md +0 -31
- package/src/skills/official/official-harness-engineering.md +0 -108
- package/src/skills/official/official-performance-optimization.md +0 -30
- package/src/skills/official/official-pr-review.md +0 -35
- package/src/skills/official/official-release-checklist.md +0 -30
- package/src/skills/official/official-security-hardening.md +0 -32
- package/src/skills/official/official-spec-driven-design.md +0 -31
- package/src/skills/official/planning-with-files.md +0 -241
- package/src/skills/official/ui-ux-pro-max.md +0 -105
- package/src/skills/official/webapp-testing.md +0 -96
- package/src/skills/official-skills.ts +0 -89
- package/src/skills/registry.ts +0 -355
- package/src/skills/semantic-matcher.ts +0 -234
- package/src/skills/tools/pipeline-suggest.ts +0 -226
- package/src/skills/tools/skill-invoke.ts +0 -168
- package/src/skills/tools/skill-list.ts +0 -59
- package/src/skills/upgrade-engine.ts +0 -541
- package/src/skills/upgrade-prompt.ts +0 -84
- package/src/templates/go.yaml +0 -53
- package/src/templates/python.yaml +0 -59
- package/src/templates/react.yaml +0 -55
- package/src/templates/template-manager.ts +0 -170
- package/src/web/analytics/anti-pattern-detector.ts +0 -367
- package/src/web/analytics/drift-detector.ts +0 -219
- package/src/web/analytics/weekly-report.ts +0 -431
- package/src/web/auth-middleware.ts +0 -54
- package/src/web/routes/_helpers.ts +0 -34
- package/src/web/routes/ai.ts +0 -204
- package/src/web/routes/auth.ts +0 -22
- package/src/web/routes/drift.ts +0 -25
- package/src/web/routes/error-handler.ts +0 -120
- package/src/web/routes/events.ts +0 -47
- package/src/web/routes/insights.ts +0 -43
- package/src/web/routes/patch.ts +0 -117
- package/src/web/routes/reports.ts +0 -34
- package/src/web/routes/rules.ts +0 -76
- package/src/web/routes/sessions.ts +0 -250
- package/src/web/routes/skill-stats.ts +0 -92
- package/src/web/routes/skills.ts +0 -350
- package/src/web/routes/static.ts +0 -67
- package/src/web/routes/stats.ts +0 -50
- package/src/web/routes/status.ts +0 -30
- package/src/web/routes/tasks.ts +0 -193
- package/src/web/routes/token-usage.ts +0 -20
- package/src/web/routes/trace.ts +0 -126
- package/src/web/routes/types.ts +0 -57
- package/src/web/server.ts +0 -134
- package/src/web/ssrf-guard.ts +0 -112
- package/src/web/static/index.html +0 -3251
- package/src/web/static/vendor/chart.umd.min.js +0 -20
- package/tests/e2e/dashboard.spec.ts +0 -205
- package/tests/e2e/routing-skill-e2e.test.ts +0 -39
- package/tests/helpers/mock-ai.ts +0 -92
- package/tests/helpers/mock-storage.ts +0 -159
- package/tests/integration/claudemd-generator.test.ts +0 -90
- package/tests/integration/queue-replay.integration.test.ts +0 -193
- package/tests/integration/tasks-filter.integration.test.ts +0 -154
- package/tests/integration/web-analytics.integration.test.ts +0 -133
- package/tests/integration/web-stats.integration.test.ts +0 -135
- package/tests/integration/web-trace.integration.test.ts +0 -175
- package/tests/performance/database.benchmark.ts +0 -161
- package/tests/semantic-matcher.test.ts +0 -99
- package/tests/skill-matcher.test.ts +0 -110
- package/tests/unit/ai-provider-retry.test.ts +0 -194
- package/tests/unit/ai-provider-vision.test.ts +0 -224
- package/tests/unit/claudemd-generator.test.ts +0 -68
- package/tests/unit/cli-mcp.test.ts +0 -141
- package/tests/unit/core/forge-paths.test.ts +0 -99
- package/tests/unit/daemon/hook-sync.test.ts +0 -71
- package/tests/unit/daemon/post-tool-use.test.ts +0 -121
- package/tests/unit/daemon/skill-sync.test.ts +0 -75
- package/tests/unit/daemon/stop-handler-behavior-summary.test.ts +0 -202
- package/tests/unit/daemon/task-segmenter-recover.test.ts +0 -84
- package/tests/unit/event-fields.test.ts +0 -88
- package/tests/unit/event-parser.test.ts +0 -55
- package/tests/unit/handlers.test.ts +0 -171
- package/tests/unit/hooks/resolve-project-path.test.ts +0 -122
- package/tests/unit/invocation-guard.test.ts +0 -125
- package/tests/unit/queue.test.ts +0 -272
- package/tests/unit/router.test.ts +0 -138
- package/tests/unit/security.test.ts +0 -128
- package/tests/unit/skill-invocations-workflow.test.ts +0 -495
- package/tests/unit/skill-registry.test.ts +0 -94
- package/tests/unit/skills/invocation-guard-ttl.test.ts +0 -211
- package/tests/unit/skills/official-skills-loader.test.ts +0 -126
- package/tests/unit/skills/registry-multiformat.test.ts +0 -92
- package/tests/unit/skills/upgrade-engine-parse.test.ts +0 -138
- package/tests/unit/skills/upgrade-engine.test.ts +0 -401
- package/tests/unit/skills/upgrade-prompt.test.ts +0 -89
- package/tests/unit/socket-server.test.ts +0 -183
- package/tests/unit/storage/event-operations-aggregates.test.ts +0 -342
- package/tests/unit/storage/migration-idempotent.test.ts +0 -304
- package/tests/unit/storage/routing-aggregates.test.ts +0 -276
- package/tests/unit/storage/routing.test.ts +0 -117
- package/tests/unit/storage/schema-missing.test.ts +0 -81
- package/tests/unit/storage/session-operations-aggregates.test.ts +0 -120
- package/tests/unit/storage/sessions-aggregate.test.ts +0 -435
- package/tests/unit/storage/skill-operations-counts.test.ts +0 -106
- package/tests/unit/storage/skills-aggregates.test.ts +0 -104
- package/tests/unit/storage/sqlite-refactor-harness.test.ts +0 -314
- package/tests/unit/storage/task-operations-counts.test.ts +0 -46
- package/tests/unit/storage/tasks-getById.test.ts +0 -343
- package/tests/unit/storage/tasks-stale-gc.test.ts +0 -86
- package/tests/unit/storage.test.ts +0 -172
- package/tests/unit/token-usage.test.ts +0 -144
- package/tests/unit/type-guards.test.ts +0 -201
- package/tests/unit/utils/format.test.ts +0 -189
- package/tests/unit/utils/session.test.ts +0 -89
- package/tests/unit/utils/time.test.ts +0 -112
- package/tests/unit/web/navigation-back-contract.test.ts +0 -134
- package/tests/unit/web/routes-auth.test.ts +0 -93
- package/tests/unit/web/routes-events.test.ts +0 -101
- package/tests/unit/web/routes-rules.test.ts +0 -182
- package/tests/unit/web/routes-sessions.test.ts +0 -181
- package/tests/unit/web/routes-skill-stats.test.ts +0 -179
- package/tests/unit/web/routes-stats.test.ts +0 -92
- package/tests/unit/web/routes-tasks.test.ts +0 -385
- package/tests/unit/web/task-title-contract.test.ts +0 -210
- package/tests/unit/web/tasks-component-contract.test.ts +0 -179
- package/tsconfig.json +0 -22
- package/vitest.config.ts +0 -21
- package/vitest.integration.config.ts +0 -16
- package/web/CLAUDE.md +0 -20
- package/web/index.html +0 -13
- package/web/package-lock.json +0 -4854
- package/web/package.json +0 -35
- package/web/postcss.config.js +0 -6
- package/web/src/App.tsx +0 -110
- package/web/src/components/CodeBlock.tsx +0 -31
- package/web/src/components/Confirm.tsx +0 -96
- package/web/src/components/Drawer.tsx +0 -60
- package/web/src/components/Layout.tsx +0 -145
- package/web/src/components/MarkdownRenderer.tsx +0 -77
- package/web/src/components/SearchInput.tsx +0 -31
- package/web/src/components/SessionDetailContent.tsx +0 -157
- package/web/src/components/Toast.tsx +0 -92
- package/web/src/index.css +0 -19
- package/web/src/main.tsx +0 -31
- package/web/src/pages/AIConfig.tsx +0 -233
- package/web/src/pages/Dashboard.tsx +0 -572
- package/web/src/pages/Events.tsx +0 -271
- package/web/src/pages/Reports.tsx +0 -428
- package/web/src/pages/SessionDetail.tsx +0 -162
- package/web/src/pages/Sessions.tsx +0 -205
- package/web/src/pages/Skills.tsx +0 -180
- package/web/src/pages/TaskDetail.tsx +0 -515
- package/web/src/pages/Tasks.tsx +0 -415
- package/web/src/utils/auth.ts +0 -59
- package/web/src/utils/export.ts +0 -54
- package/web/src/utils/navigation.ts +0 -25
- package/web/src/utils/task-title.ts +0 -49
- package/web/src/utils/time.ts +0 -13
- package/web/tailwind.config.js +0 -11
- package/web/tsconfig.json +0 -21
- package/web/tsconfig.node.json +0 -10
- package/web/vite.config.ts +0 -76
- package/winspan-claude-forge-8.43.0.tgz +0 -0
|
@@ -1,568 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CLAUDE.md Generator — AI 驱动的 CLAUDE.md 生成与管理
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { readFileSync, writeFileSync, existsSync, readdirSync, statSync } from 'node:fs';
|
|
6
|
-
import { dirname, join } from 'node:path';
|
|
7
|
-
import { fileURLToPath } from 'node:url';
|
|
8
|
-
import type { ClaudeProvider } from '../core/ai/provider.js';
|
|
9
|
-
import { TechDetector, type TechStack } from './tech-detector.js';
|
|
10
|
-
|
|
11
|
-
const AUTO_START = '<!-- forge:claudemd-auto -->';
|
|
12
|
-
const AUTO_END = '<!-- forge:end-claudemd-auto -->';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Swarm Protocol — fixed CLAUDE.md preface injected into every project.
|
|
16
|
-
*
|
|
17
|
-
* Loaded from `templates/swarm-protocol.md` (same directory as this module's
|
|
18
|
-
* compiled output). `fileURLToPath(import.meta.url)` resolves to either
|
|
19
|
-
* `src/claudemd/` (dev via tsx) or `dist/claudemd/` (npm install) — both
|
|
20
|
-
* have a sibling `templates/` directory after build (see package.json `build`
|
|
21
|
-
* script which copies *.md into dist/claudemd/templates/).
|
|
22
|
-
*
|
|
23
|
-
* Cached at module level: the file is read at most once per process.
|
|
24
|
-
*/
|
|
25
|
-
let _swarmProtocolCache: string | null = null;
|
|
26
|
-
function getSwarmProtocol(): string {
|
|
27
|
-
if (_swarmProtocolCache !== null) return _swarmProtocolCache;
|
|
28
|
-
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
29
|
-
const p = join(thisDir, 'templates', 'swarm-protocol.md');
|
|
30
|
-
if (!existsSync(p)) {
|
|
31
|
-
throw new Error(
|
|
32
|
-
`[ClaudeMdGenerator] swarm-protocol.md not found at ${p}. ` +
|
|
33
|
-
`This indicates a broken build/install. Run \`npm run build\` or reinstall the package.`,
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
_swarmProtocolCache = readFileSync(p, 'utf-8');
|
|
37
|
-
return _swarmProtocolCache;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export class ClaudeMdGenerator {
|
|
41
|
-
readonly detector: TechDetector;
|
|
42
|
-
|
|
43
|
-
constructor(
|
|
44
|
-
private ai: ClaudeProvider,
|
|
45
|
-
detector?: TechDetector,
|
|
46
|
-
) {
|
|
47
|
-
this.detector = detector ?? new TechDetector();
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* 检测技术栈 + 生成 CLAUDE.md
|
|
52
|
-
*
|
|
53
|
-
* 策略:Swarm 协议是固定内容(不依赖 AI),项目信息尝试用 AI 生成,
|
|
54
|
-
* 失败时回退到基于技术栈检测的模板。
|
|
55
|
-
*/
|
|
56
|
-
async generate(projectPath: string, userContent?: string): Promise<string> {
|
|
57
|
-
const stack = this.detector.detect(projectPath);
|
|
58
|
-
|
|
59
|
-
// 1. 尝试 AI 生成项目特定内容
|
|
60
|
-
let projectContent = '';
|
|
61
|
-
try {
|
|
62
|
-
const prompt = this.buildPrompt(stack, projectPath);
|
|
63
|
-
const aiResult = await this.ai.complete(prompt, {
|
|
64
|
-
system: 'You are a technical writer. Output ONLY markdown content for a CLAUDE.md file. Do NOT ask questions or explain what you will do. Start directly with ## headers.',
|
|
65
|
-
maxTokens: 4096,
|
|
66
|
-
// 4096-token 生成在转发网关(如 one.iflytek 等)下容易超过默认 30s;
|
|
67
|
-
// 给 CLAUDE.md 生成单独拉长超时,失败再回退到本地模板。
|
|
68
|
-
timeoutMs: 120_000,
|
|
69
|
-
});
|
|
70
|
-
// 验证 AI 输出是否有效(至少包含一个 ## 标题)
|
|
71
|
-
if (aiResult && aiResult.includes('##')) {
|
|
72
|
-
// AI 经常在"目录结构"章节退化成 project-root/com/example/project 模板,
|
|
73
|
-
// 不可信任;强制替换为真实扫描结果。
|
|
74
|
-
projectContent = this.overrideDirectoryStructure(aiResult, stack, projectPath);
|
|
75
|
-
}
|
|
76
|
-
} catch {
|
|
77
|
-
// AI 失败,使用模板回退
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// 2. 如果 AI 失败或返回无效内容,使用模板
|
|
81
|
-
if (!projectContent) {
|
|
82
|
-
projectContent = this.buildFallbackContent(stack, projectPath);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// 3. Swarm 协议 + 项目内容
|
|
86
|
-
const full = getSwarmProtocol() + '\n\n' + projectContent;
|
|
87
|
-
return this.merge(full, userContent);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* 基于技术栈检测的回退模板(不依赖 AI)
|
|
92
|
-
*
|
|
93
|
-
* 即使 AI 调用失败,也要生成足够丰富的项目指引,让 Claude Code 拿到:
|
|
94
|
-
* - 技术栈概览(含版本、包管理器、测试 / Linter)
|
|
95
|
-
* - 构建 / 测试 / 运行命令(按包管理器分发)
|
|
96
|
-
* - 目录结构(扫描项目根目录真实结构)
|
|
97
|
-
* - 代码规范(命名、提交信息、PR 流程)
|
|
98
|
-
* - 常见陷阱(按技术栈定制)
|
|
99
|
-
* - 贡献指南
|
|
100
|
-
*/
|
|
101
|
-
private buildFallbackContent(stack: TechStack, projectPath: string): string {
|
|
102
|
-
const sections: string[] = [];
|
|
103
|
-
|
|
104
|
-
sections.push(this.sectionOverview(stack));
|
|
105
|
-
sections.push(this.sectionTechStack(stack));
|
|
106
|
-
sections.push(this.sectionBuildCommands(stack));
|
|
107
|
-
sections.push(this.sectionDirectoryStructure(stack, projectPath));
|
|
108
|
-
sections.push(this.sectionCodingStandards(stack));
|
|
109
|
-
sections.push(this.sectionCommonPitfalls(stack));
|
|
110
|
-
sections.push(this.sectionContributionGuide(stack));
|
|
111
|
-
|
|
112
|
-
return sections.filter(Boolean).join('\n\n');
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
private sectionOverview(stack: TechStack): string {
|
|
116
|
-
const lang = stack.language[0] || '未识别';
|
|
117
|
-
const framework = stack.framework.length > 0 ? ` + ${stack.framework.join(' / ')}` : '';
|
|
118
|
-
return [
|
|
119
|
-
'## 项目概述',
|
|
120
|
-
'',
|
|
121
|
-
`本项目基于 ${lang}${framework} 构建。请根据实际业务补充项目目标、核心模块职责与上下游依赖。`,
|
|
122
|
-
].join('\n');
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
private sectionTechStack(stack: TechStack): string {
|
|
126
|
-
const lines: string[] = ['## 技术栈', ''];
|
|
127
|
-
|
|
128
|
-
if (stack.language.length > 0) {
|
|
129
|
-
lines.push(`- **语言**: ${stack.language.join(', ')}`);
|
|
130
|
-
}
|
|
131
|
-
if (stack.framework.length > 0) {
|
|
132
|
-
const descs = stack.framework.map(fw => `${fw}${this.frameworkHint(fw)}`);
|
|
133
|
-
lines.push(`- **框架**: ${descs.join(', ')}`);
|
|
134
|
-
}
|
|
135
|
-
lines.push(`- **包管理器**: ${stack.packageManager}`);
|
|
136
|
-
if (stack.testFramework) {
|
|
137
|
-
lines.push(`- **测试框架**: ${stack.testFramework}`);
|
|
138
|
-
}
|
|
139
|
-
if (stack.linter) {
|
|
140
|
-
lines.push(`- **代码检查**: ${stack.linter}`);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const ecosystem = this.ecosystemNotes(stack);
|
|
144
|
-
if (ecosystem) {
|
|
145
|
-
lines.push('', ecosystem);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return lines.join('\n');
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
private frameworkHint(fw: string): string {
|
|
152
|
-
const hints: Record<string, string> = {
|
|
153
|
-
'Spring Boot': '(IoC 容器 + 自动配置,所有组件通过注解注入)',
|
|
154
|
-
MyBatis: '(SQL 映射,Mapper 接口 + XML)',
|
|
155
|
-
Dubbo: '(RPC 框架,服务注册与发现)',
|
|
156
|
-
React: '(组件化 UI,单向数据流)',
|
|
157
|
-
'Next.js': '(React 全栈框架,支持 SSR / SSG)',
|
|
158
|
-
Vue: '(渐进式 UI 框架)',
|
|
159
|
-
Express: '(Node.js Web 框架,中间件模型)',
|
|
160
|
-
Fastify: '(高性能 Node.js Web 框架)',
|
|
161
|
-
NestJS: '(Node.js 企业级框架,装饰器 + DI)',
|
|
162
|
-
};
|
|
163
|
-
return hints[fw] || '';
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
private ecosystemNotes(stack: TechStack): string {
|
|
167
|
-
if (stack.language.some(l => l.startsWith('Java'))) {
|
|
168
|
-
return '> Java 生态约定:包名全小写,类名 PascalCase,方法 camelCase;Maven / Gradle 依赖统一在顶层 pom.xml / build.gradle 管理,子模块只声明坐标不重复版本。';
|
|
169
|
-
}
|
|
170
|
-
if (stack.language.includes('TypeScript')) {
|
|
171
|
-
return '> TypeScript 生态约定:开启 strict 模式,模块边界用 interface,内部用 type;async/await 优先,禁止混用 Promise 链。';
|
|
172
|
-
}
|
|
173
|
-
if (stack.language.some(l => l.startsWith('Go'))) {
|
|
174
|
-
return '> Go 生态约定:使用 gofmt 自动格式化;错误值必须显式处理,不得丢弃;包名全小写且与目录名一致。';
|
|
175
|
-
}
|
|
176
|
-
if (stack.language.includes('Rust')) {
|
|
177
|
-
return '> Rust 生态约定:`cargo fmt` + `cargo clippy` 双重检查;尽量使用 Result / Option,避免 panic;mod.rs 或 `<name>.rs` 二选一。';
|
|
178
|
-
}
|
|
179
|
-
if (stack.language.includes('Python')) {
|
|
180
|
-
return '> Python 生态约定:遵循 PEP8;建议启用 mypy / ruff;虚拟环境隔离依赖。';
|
|
181
|
-
}
|
|
182
|
-
return '';
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
private sectionBuildCommands(stack: TechStack): string {
|
|
186
|
-
const lines: string[] = ['## 构建与测试', '', '```bash'];
|
|
187
|
-
const pm = stack.packageManager;
|
|
188
|
-
|
|
189
|
-
if (pm === 'npm') {
|
|
190
|
-
lines.push('# 安装依赖', 'npm install', '', '# 开发模式(如适用)', 'npm run dev', '', '# 构建产物', 'npm run build', '', '# 运行测试', 'npm test', '', '# 类型检查(TypeScript)', 'npx tsc --noEmit');
|
|
191
|
-
} else if (pm === 'pnpm') {
|
|
192
|
-
lines.push('pnpm install', 'pnpm dev', 'pnpm build', 'pnpm test');
|
|
193
|
-
} else if (pm === 'yarn') {
|
|
194
|
-
lines.push('yarn', 'yarn dev', 'yarn build', 'yarn test');
|
|
195
|
-
} else if (pm === 'bun') {
|
|
196
|
-
lines.push('bun install', 'bun run dev', 'bun run build', 'bun test');
|
|
197
|
-
} else if (pm === 'maven') {
|
|
198
|
-
lines.push('# 清理并编译', 'mvn clean compile', '', '# 运行测试', 'mvn test', '', '# 打包(跳过测试加 -DskipTests)', 'mvn clean package', '', '# 本地安装到 ~/.m2', 'mvn clean install', '', '# 启动 Spring Boot(若适用)', 'mvn spring-boot:run');
|
|
199
|
-
} else if (pm === 'gradle') {
|
|
200
|
-
lines.push('# 构建', './gradlew build', '', '# 运行测试', './gradlew test', '', '# 清理', './gradlew clean', '', '# 启动 Spring Boot(若适用)', './gradlew bootRun');
|
|
201
|
-
} else if (pm === 'go mod') {
|
|
202
|
-
lines.push('go mod tidy', 'go build ./...', 'go test ./...', 'go vet ./...');
|
|
203
|
-
} else if (pm === 'cargo') {
|
|
204
|
-
lines.push('cargo build', 'cargo test', 'cargo clippy -- -D warnings', 'cargo fmt --check');
|
|
205
|
-
} else if (pm === 'pip' || pm === 'poetry' || pm === 'pipenv') {
|
|
206
|
-
const prefix = pm === 'poetry' ? 'poetry run ' : pm === 'pipenv' ? 'pipenv run ' : '';
|
|
207
|
-
lines.push(`${prefix}pytest`, `${prefix}python -m build`);
|
|
208
|
-
} else {
|
|
209
|
-
lines.push('# 请补充构建与测试命令');
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
lines.push('```');
|
|
213
|
-
return lines.join('\n');
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
private sectionDirectoryStructure(stack: TechStack, projectPath: string): string {
|
|
217
|
-
// Java 项目需要更深的层级才能看到包结构
|
|
218
|
-
const isJava = stack.language.some(l => l.startsWith('Java'));
|
|
219
|
-
const maxDepth = isJava ? 4 : 2;
|
|
220
|
-
const tree = this.scanDirectoryTree(projectPath, maxDepth, isJava);
|
|
221
|
-
if (!tree) return '';
|
|
222
|
-
return ['## 目录结构', '', '```', tree, '```'].join('\n');
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* 扫描项目根目录,生成树形结构(过滤噪声目录)
|
|
227
|
-
*/
|
|
228
|
-
private scanDirectoryTree(projectPath: string, maxDepth: number, addComments = false): string {
|
|
229
|
-
const IGNORE = new Set([
|
|
230
|
-
'node_modules', '.git', '.idea', '.vscode', '.DS_Store',
|
|
231
|
-
'dist', 'build', 'target', 'out', '.next', '.nuxt',
|
|
232
|
-
'__pycache__', '.pytest_cache', '.mypy_cache',
|
|
233
|
-
'vendor', 'coverage', '.nyc_output', 'tmp', 'temp',
|
|
234
|
-
]);
|
|
235
|
-
|
|
236
|
-
// 深目录噪声:这些目录即使不在 IGNORE 里,也不递归进去(避免爆炸)
|
|
237
|
-
const SHALLOW_ONLY = new Set([
|
|
238
|
-
'executions', 'by-route', 'history', 'logs', 'cache',
|
|
239
|
-
]);
|
|
240
|
-
|
|
241
|
-
// Java 项目目录注释映射
|
|
242
|
-
const JAVA_COMMENTS: Record<string, string> = {
|
|
243
|
-
'controller': '# 控制器层',
|
|
244
|
-
'service': '# 服务层',
|
|
245
|
-
'impl': '# 服务实现类',
|
|
246
|
-
'mapper': '# MyBatis Mapper 接口',
|
|
247
|
-
'entity': '# 实体类',
|
|
248
|
-
'dto': '# 数据传输对象',
|
|
249
|
-
'vo': '# 视图对象',
|
|
250
|
-
'config': '# 配置类',
|
|
251
|
-
'util': '# 工具类',
|
|
252
|
-
'utils': '# 工具类',
|
|
253
|
-
'common': '# 公共模块',
|
|
254
|
-
'exception': '# 异常类',
|
|
255
|
-
'interceptor': '# 拦截器',
|
|
256
|
-
'filter': '# 过滤器',
|
|
257
|
-
'aspect': '# 切面',
|
|
258
|
-
'resources': '# 配置文件、静态资源',
|
|
259
|
-
'application.yml': '# 应用配置文件',
|
|
260
|
-
'application.yaml': '# 应用配置文件',
|
|
261
|
-
'pom.xml': '# Maven 配置文件',
|
|
262
|
-
'build.gradle': '# Gradle 配置文件',
|
|
263
|
-
'README.md': '# 项目说明文档',
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
const lines: string[] = [];
|
|
267
|
-
|
|
268
|
-
const walk = (dir: string, prefix: string, depth: number) => {
|
|
269
|
-
if (depth > maxDepth) return;
|
|
270
|
-
|
|
271
|
-
let entries: string[] = [];
|
|
272
|
-
try {
|
|
273
|
-
entries = readdirSync(dir).filter(e => !IGNORE.has(e));
|
|
274
|
-
} catch {
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// 文件在前,目录在后
|
|
279
|
-
const stats = entries.map(e => {
|
|
280
|
-
try {
|
|
281
|
-
return { name: e, isDir: statSync(join(dir, e)).isDirectory() };
|
|
282
|
-
} catch {
|
|
283
|
-
return { name: e, isDir: false };
|
|
284
|
-
}
|
|
285
|
-
});
|
|
286
|
-
const files = stats.filter(s => !s.isDir).map(s => s.name);
|
|
287
|
-
const dirs = stats.filter(s => s.isDir).map(s => s.name);
|
|
288
|
-
const sorted = [...dirs.sort(), ...files.sort()];
|
|
289
|
-
|
|
290
|
-
sorted.forEach((name, idx) => {
|
|
291
|
-
const isLast = idx === sorted.length - 1;
|
|
292
|
-
const connector = isLast ? '└── ' : '├── ';
|
|
293
|
-
const childPrefix = isLast ? ' ' : '│ ';
|
|
294
|
-
|
|
295
|
-
const fullPath = join(dir, name);
|
|
296
|
-
let isDir = false;
|
|
297
|
-
try {
|
|
298
|
-
isDir = statSync(fullPath).isDirectory();
|
|
299
|
-
} catch {
|
|
300
|
-
// ignore
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// 构建行:名称 + 可选注释
|
|
304
|
-
let line = prefix + connector + name + (isDir ? '/' : '');
|
|
305
|
-
if (addComments) {
|
|
306
|
-
const comment = JAVA_COMMENTS[name] || JAVA_COMMENTS[name.toLowerCase()];
|
|
307
|
-
if (comment) {
|
|
308
|
-
// 对齐注释(假设最长路径 60 字符)
|
|
309
|
-
const padding = Math.max(1, 60 - line.length);
|
|
310
|
-
line += ' '.repeat(padding) + comment;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
lines.push(line);
|
|
314
|
-
|
|
315
|
-
// 递归条件:是目录 + 未达深度上限 + 不在浅层名单里
|
|
316
|
-
if (isDir && depth < maxDepth && !SHALLOW_ONLY.has(name)) {
|
|
317
|
-
walk(fullPath, prefix + childPrefix, depth + 1);
|
|
318
|
-
}
|
|
319
|
-
});
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
// 根目录名:从路径提取真实项目名
|
|
323
|
-
const rootName = projectPath.split('/').filter(Boolean).pop() || 'project';
|
|
324
|
-
lines.push(rootName + '/');
|
|
325
|
-
walk(projectPath, '', 1);
|
|
326
|
-
|
|
327
|
-
return lines.join('\n');
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
private sectionCodingStandards(stack: TechStack): string {
|
|
331
|
-
const isJava = stack.language.some(l => l.startsWith('Java'));
|
|
332
|
-
const isTs = stack.language.includes('TypeScript');
|
|
333
|
-
|
|
334
|
-
const lines: string[] = ['## 代码规范', ''];
|
|
335
|
-
|
|
336
|
-
lines.push('### 命名约定', '');
|
|
337
|
-
if (isJava) {
|
|
338
|
-
lines.push('- 包名:全小写,使用反向域名(`com.company.project.module`)');
|
|
339
|
-
lines.push('- 类名:PascalCase(`UserService`、`OrderController`)');
|
|
340
|
-
lines.push('- 方法名 / 变量名:camelCase(`getUserById`、`orderList`)');
|
|
341
|
-
lines.push('- 常量:UPPER_SNAKE_CASE(`MAX_RETRY_COUNT`)');
|
|
342
|
-
lines.push('- DTO 后缀:`*Request` / `*Response` / `*DTO` / `*VO` 根据团队约定');
|
|
343
|
-
} else if (isTs) {
|
|
344
|
-
lines.push('- 文件名:kebab-case(`user-service.ts`)或 camelCase(视团队约定)');
|
|
345
|
-
lines.push('- 类型 / 接口:PascalCase(`interface User`、`type OrderStatus`)');
|
|
346
|
-
lines.push('- 函数 / 变量:camelCase');
|
|
347
|
-
lines.push('- 常量:UPPER_SNAKE_CASE 或 camelCase(不可变配置)');
|
|
348
|
-
} else {
|
|
349
|
-
lines.push('- 遵循所在语言生态的官方风格指南');
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
lines.push('', '### 代码组织', '');
|
|
353
|
-
lines.push('- 单文件职责单一,超过 300 行考虑拆分');
|
|
354
|
-
lines.push('- 避免循环依赖;模块边界清晰');
|
|
355
|
-
lines.push('- 公共工具函数沉淀到 `utils` / `common`,不重复造轮子');
|
|
356
|
-
lines.push('- **先读再改**:修改已有文件前必须先 Read,理解上下文再编辑');
|
|
357
|
-
|
|
358
|
-
lines.push('', '### 注释', '');
|
|
359
|
-
lines.push('- 默认不写注释;仅在"为什么这么做"不明显时补充');
|
|
360
|
-
lines.push('- 不要写"这个函数做什么"(代码本身应该自解释)');
|
|
361
|
-
lines.push('- 禁止遗留 `// TODO` 不跟单号、`// 临时` 不标日期');
|
|
362
|
-
|
|
363
|
-
lines.push('', '### 错误处理', '');
|
|
364
|
-
if (isJava) {
|
|
365
|
-
lines.push('- 优先抛出明确的业务异常,不吞异常');
|
|
366
|
-
lines.push('- Controller 层统一异常处理器(`@ControllerAdvice`)');
|
|
367
|
-
lines.push('- 事务边界清晰:`@Transactional` 只标注 Service 层');
|
|
368
|
-
} else if (isTs) {
|
|
369
|
-
lines.push('- async/await 配合 try/catch,不要混用 Promise 链');
|
|
370
|
-
lines.push('- 错误类型显式声明,不要 throw 裸字符串');
|
|
371
|
-
lines.push('- 边界层(HTTP handler、消息消费者)必须有统一错误出口');
|
|
372
|
-
} else {
|
|
373
|
-
lines.push('- 显式处理每一个错误返回值,不静默吞异常');
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
return lines.join('\n');
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
private sectionCommonPitfalls(stack: TechStack): string {
|
|
380
|
-
const pitfalls: string[] = [];
|
|
381
|
-
|
|
382
|
-
if (stack.framework.includes('Spring Boot')) {
|
|
383
|
-
pitfalls.push('- **循环依赖**:优先重构为单向依赖,不要用 `@Lazy` 掩盖');
|
|
384
|
-
pitfalls.push('- **配置覆盖**:`application-{profile}.yml` 会覆盖 `application.yml`,排查配置问题时两边都要看');
|
|
385
|
-
pitfalls.push('- **事务失效**:`@Transactional` 同类内部方法调用不生效,需要通过 Spring 代理调用');
|
|
386
|
-
}
|
|
387
|
-
if (stack.framework.includes('MyBatis')) {
|
|
388
|
-
pitfalls.push('- **Mapper XML `#{}` vs `${}`**:`${}` 会导致 SQL 注入,只用于表名 / 列名等元数据');
|
|
389
|
-
pitfalls.push('- **N+1 查询**:使用 `<resultMap>` + `<collection>` 一次 JOIN 获取,避免循环查询');
|
|
390
|
-
}
|
|
391
|
-
if (stack.language.includes('TypeScript')) {
|
|
392
|
-
pitfalls.push('- **类型断言滥用**:`as unknown as T` 通常是设计问题的信号,优先修正类型定义');
|
|
393
|
-
pitfalls.push('- **Promise 未 await**:async 函数返回 Promise,调用方忘记 await 会导致错误被吞');
|
|
394
|
-
}
|
|
395
|
-
if (stack.framework.includes('React')) {
|
|
396
|
-
pitfalls.push('- **useEffect 依赖**:依赖数组遗漏变量会导致闭包陷阱;使用 ESLint `exhaustive-deps` 规则');
|
|
397
|
-
pitfalls.push('- **列表 key**:不要用 index 作为 key,列表重排时会错乱');
|
|
398
|
-
}
|
|
399
|
-
if (stack.language.some(l => l.startsWith('Go'))) {
|
|
400
|
-
pitfalls.push('- **goroutine 泄漏**:启动 goroutine 必须有退出路径(context.Done / channel close)');
|
|
401
|
-
pitfalls.push('- **for 循环变量捕获**:Go 1.21 之前循环变量在 goroutine 里共享,需要局部变量拷贝');
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
if (pitfalls.length === 0) {
|
|
405
|
-
pitfalls.push('- 依赖升级前先运行全量测试');
|
|
406
|
-
pitfalls.push('- 修改公共接口前先 grep 所有调用方,评估影响面');
|
|
407
|
-
pitfalls.push('- 禁止在 main 分支直接提交,所有改动走 PR / MR 流程');
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
return ['## 常见陷阱', '', ...pitfalls].join('\n');
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
private sectionContributionGuide(stack: TechStack): string {
|
|
414
|
-
const isJava = stack.language.some(l => l.startsWith('Java'));
|
|
415
|
-
const lines: string[] = ['## 贡献指南', ''];
|
|
416
|
-
|
|
417
|
-
lines.push('### 分支策略', '');
|
|
418
|
-
lines.push('- `main` / `master`:受保护分支,只接受 PR 合并');
|
|
419
|
-
lines.push('- `feature/<ticket>-<slug>`:新功能开发');
|
|
420
|
-
lines.push('- `fix/<ticket>-<slug>`:Bug 修复');
|
|
421
|
-
lines.push('- `refactor/<scope>`:重构');
|
|
422
|
-
|
|
423
|
-
lines.push('', '### 提交信息(Conventional Commits)', '');
|
|
424
|
-
lines.push('```');
|
|
425
|
-
lines.push('<type>(<scope>): <subject>');
|
|
426
|
-
lines.push('');
|
|
427
|
-
lines.push('类型(type):feat / fix / refactor / perf / test / docs / chore / style');
|
|
428
|
-
lines.push('范围(scope):模块或目录名,例如 auth / api / ui');
|
|
429
|
-
lines.push('主题(subject):50 字以内,祈使句,不加句号');
|
|
430
|
-
lines.push('```');
|
|
431
|
-
lines.push('');
|
|
432
|
-
lines.push('示例:`feat(auth): 新增 OAuth2 Google 登录`');
|
|
433
|
-
|
|
434
|
-
lines.push('', '### PR / MR 检查清单', '');
|
|
435
|
-
lines.push('- [ ] 通过所有自动化测试');
|
|
436
|
-
if (isJava) {
|
|
437
|
-
lines.push('- [ ] 通过 `mvn verify` / `./gradlew check`');
|
|
438
|
-
} else if (stack.language.includes('TypeScript')) {
|
|
439
|
-
lines.push('- [ ] 通过 `npx tsc --noEmit`(类型检查)');
|
|
440
|
-
if (stack.linter) lines.push(`- [ ] 通过 ${stack.linter} 检查`);
|
|
441
|
-
}
|
|
442
|
-
lines.push('- [ ] 新增代码有对应测试(除纯文档 / 配置外)');
|
|
443
|
-
lines.push('- [ ] 公共接口变更已更新文档');
|
|
444
|
-
lines.push('- [ ] 无敏感信息(密钥、token、内网地址)');
|
|
445
|
-
lines.push('- [ ] 经过至少一位 reviewer 审核');
|
|
446
|
-
|
|
447
|
-
return lines.join('\n');
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
/**
|
|
451
|
-
* 写入 CLAUDE.md,保留用户手动编辑的内容
|
|
452
|
-
*/
|
|
453
|
-
write(projectPath: string, content: string): void {
|
|
454
|
-
const claudeMdPath = join(projectPath, 'CLAUDE.md');
|
|
455
|
-
const { user } = this.read(projectPath);
|
|
456
|
-
|
|
457
|
-
const autoBlock = `${AUTO_START}\n${content.trim()}\n${AUTO_END}`;
|
|
458
|
-
const final = user
|
|
459
|
-
? `${autoBlock}\n\n${user}\n`
|
|
460
|
-
: `${autoBlock}\n`;
|
|
461
|
-
|
|
462
|
-
writeFileSync(claudeMdPath, final, 'utf-8');
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
/**
|
|
466
|
-
* 读取并分离自动生成和用户内容
|
|
467
|
-
*/
|
|
468
|
-
read(projectPath: string): { auto: string; user: string } {
|
|
469
|
-
const claudeMdPath = join(projectPath, 'CLAUDE.md');
|
|
470
|
-
if (!existsSync(claudeMdPath)) {
|
|
471
|
-
return { auto: '', user: '' };
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
const content = readFileSync(claudeMdPath, 'utf-8');
|
|
475
|
-
const startIdx = content.indexOf(AUTO_START);
|
|
476
|
-
const endIdx = content.indexOf(AUTO_END);
|
|
477
|
-
|
|
478
|
-
if (startIdx === -1 || endIdx === -1) {
|
|
479
|
-
return { auto: '', user: content.trim() };
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
const auto = content
|
|
483
|
-
.substring(startIdx + AUTO_START.length, endIdx)
|
|
484
|
-
.trim();
|
|
485
|
-
|
|
486
|
-
const before = content.substring(0, startIdx).trim();
|
|
487
|
-
const after = content.substring(endIdx + AUTO_END.length).trim();
|
|
488
|
-
const user = [before, after].filter(Boolean).join('\n\n');
|
|
489
|
-
|
|
490
|
-
return { auto, user };
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
// ── Private Methods ────────────────────────────────────────────────────
|
|
494
|
-
|
|
495
|
-
private buildPrompt(stack: TechStack, projectPath: string): string {
|
|
496
|
-
const projectName = projectPath.split('/').filter(Boolean).pop() || 'project';
|
|
497
|
-
const isJava = stack.language.some(l => l.startsWith('Java'));
|
|
498
|
-
const realTree = this.scanDirectoryTree(projectPath, isJava ? 4 : 2, isJava);
|
|
499
|
-
|
|
500
|
-
const sections = [
|
|
501
|
-
`请为项目 "${projectName}" 生成 CLAUDE.md 文件内容,包含以下章节:`,
|
|
502
|
-
'',
|
|
503
|
-
'1. 项目概述',
|
|
504
|
-
'2. 技术栈',
|
|
505
|
-
'3. 开发规范',
|
|
506
|
-
'4. 构建与测试命令',
|
|
507
|
-
'5. 目录结构(**必须**直接使用下方给出的真实目录树,不得编造或改名)',
|
|
508
|
-
'6. 代码风格',
|
|
509
|
-
'7. 常见陷阱',
|
|
510
|
-
'8. 贡献指南',
|
|
511
|
-
'',
|
|
512
|
-
'## 检测到的技术栈',
|
|
513
|
-
'',
|
|
514
|
-
`- 语言: ${stack.language.join(', ') || '未检测到'}`,
|
|
515
|
-
`- 框架: ${stack.framework.join(', ') || '无'}`,
|
|
516
|
-
`- 包管理器: ${stack.packageManager}`,
|
|
517
|
-
];
|
|
518
|
-
|
|
519
|
-
if (stack.testFramework) {
|
|
520
|
-
sections.push(`- 测试框架: ${stack.testFramework}`);
|
|
521
|
-
}
|
|
522
|
-
if (stack.linter) {
|
|
523
|
-
sections.push(`- Linter: ${stack.linter}`);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
sections.push(
|
|
527
|
-
'',
|
|
528
|
-
'## 项目真实目录结构(必须原样使用)',
|
|
529
|
-
'',
|
|
530
|
-
'```',
|
|
531
|
-
realTree,
|
|
532
|
-
'```',
|
|
533
|
-
'',
|
|
534
|
-
'⚠️ 生成"目录结构"章节时,**必须**用上面这个真实目录树,禁止使用 `project-root/`、`com/example/project/` 等占位符。',
|
|
535
|
-
'',
|
|
536
|
-
'请直接输出 Markdown 内容,不要包含代码块包裹。',
|
|
537
|
-
'使用中文撰写。每个章节保持简洁,总长度控制在 200 行以内。',
|
|
538
|
-
);
|
|
539
|
-
|
|
540
|
-
return sections.join('\n');
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
/**
|
|
544
|
-
* 强制替换 AI 输出中的"目录结构"章节为真实扫描结果。
|
|
545
|
-
*
|
|
546
|
-
* AI 经常即使拿到真实目录也会退化成模板(project-root/com/example/project)。
|
|
547
|
-
* 做字符串替换是最稳妥的兜底。
|
|
548
|
-
*/
|
|
549
|
-
private overrideDirectoryStructure(content: string, stack: TechStack, projectPath: string): string {
|
|
550
|
-
const isJava = stack.language.some(l => l.startsWith('Java'));
|
|
551
|
-
const realTree = this.scanDirectoryTree(projectPath, isJava ? 4 : 2, isJava);
|
|
552
|
-
const realSection = ['## 目录结构', '', '```', realTree, '```'].join('\n');
|
|
553
|
-
|
|
554
|
-
// 匹配 "## 目录结构" 到下一个 "##" 之前的所有内容
|
|
555
|
-
const pattern = /^##\s+目录结构[\s\S]*?(?=^##\s|\Z)/m;
|
|
556
|
-
if (pattern.test(content)) {
|
|
557
|
-
return content.replace(pattern, realSection + '\n\n');
|
|
558
|
-
}
|
|
559
|
-
// 如果 AI 没生成目录结构章节,追加到末尾
|
|
560
|
-
return content.trimEnd() + '\n\n' + realSection + '\n';
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
private merge(generated: string, userContent?: string): string {
|
|
564
|
-
if (!userContent) return generated;
|
|
565
|
-
|
|
566
|
-
return [generated.trim(), '', '---', '', userContent.trim()].join('\n');
|
|
567
|
-
}
|
|
568
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ConventionExtractor — 从 CLAUDE.md 提取项目规范
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { readFileSync, existsSync } from 'node:fs';
|
|
6
|
-
import { join } from 'node:path';
|
|
7
|
-
|
|
8
|
-
const CONVENTION_START = '<!-- forge:convention-start';
|
|
9
|
-
const CONVENTION_END = '<!-- forge:convention-end';
|
|
10
|
-
|
|
11
|
-
export class ConventionExtractor {
|
|
12
|
-
/**
|
|
13
|
-
* 从 CLAUDE.md 提取规范内容
|
|
14
|
-
*/
|
|
15
|
-
extract(projectPath: string): string | null {
|
|
16
|
-
const claudeMdPath = join(projectPath, 'CLAUDE.md');
|
|
17
|
-
if (!existsSync(claudeMdPath)) return null;
|
|
18
|
-
|
|
19
|
-
const content = readFileSync(claudeMdPath, 'utf-8');
|
|
20
|
-
|
|
21
|
-
// Find convention blocks
|
|
22
|
-
const conventions: string[] = [];
|
|
23
|
-
let pos = 0;
|
|
24
|
-
|
|
25
|
-
while (true) {
|
|
26
|
-
const startIdx = content.indexOf(CONVENTION_START, pos);
|
|
27
|
-
if (startIdx === -1) break;
|
|
28
|
-
|
|
29
|
-
const endIdx = content.indexOf(CONVENTION_END, startIdx);
|
|
30
|
-
if (endIdx === -1) break;
|
|
31
|
-
|
|
32
|
-
// Extract content between markers (skip the start marker line)
|
|
33
|
-
const startLineEnd = content.indexOf('\n', startIdx);
|
|
34
|
-
if (startLineEnd === -1 || startLineEnd > endIdx) break;
|
|
35
|
-
|
|
36
|
-
const block = content.slice(startLineEnd + 1, endIdx).trim();
|
|
37
|
-
if (block) {
|
|
38
|
-
conventions.push(block);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
pos = endIdx + CONVENTION_END.length;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (conventions.length === 0) return null;
|
|
45
|
-
|
|
46
|
-
return this.format(conventions);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
private format(conventions: string[]): string {
|
|
50
|
-
const lines = [
|
|
51
|
-
'## 项目规范说明',
|
|
52
|
-
'',
|
|
53
|
-
'以下内容用于提供项目背景、原则和协作约束。运行时规则判定以 Convention YAML 为准。',
|
|
54
|
-
'',
|
|
55
|
-
];
|
|
56
|
-
|
|
57
|
-
// Merge all convention blocks
|
|
58
|
-
for (const block of conventions) {
|
|
59
|
-
lines.push(block);
|
|
60
|
-
lines.push('');
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
lines.push('---');
|
|
64
|
-
lines.push('');
|
|
65
|
-
lines.push('在开始任何工作前,请理解这些项目约束;涉及实际阻断与告警时,以结构化 Convention 规则的判定结果为准。');
|
|
66
|
-
|
|
67
|
-
return lines.join('\n');
|
|
68
|
-
}
|
|
69
|
-
}
|
package/src/claudemd/index.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CLAUDE.md Management Module — 统一入口
|
|
3
|
-
*
|
|
4
|
-
* 包含 4 个子模块:
|
|
5
|
-
* - ResumeManager: 续接信息管理(统计式,不调 AI)
|
|
6
|
-
* - TechDetector: 技术栈检测
|
|
7
|
-
* - ClaudeMdGenerator: AI 驱动的 CLAUDE.md 生成
|
|
8
|
-
* - PersonaManager: 技术栈驱动的人设管理
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import type { SQLiteStorage } from '../core/storage/sqlite.js';
|
|
12
|
-
import type { ClaudeProvider } from '../core/ai/provider.js';
|
|
13
|
-
import { ResumeManager } from './resume-manager.js';
|
|
14
|
-
import { ClaudeMdGenerator } from './claudemd-generator.js';
|
|
15
|
-
import { TechDetector } from './tech-detector.js';
|
|
16
|
-
import { PersonaManager } from './persona-manager.js';
|
|
17
|
-
|
|
18
|
-
export class ClaudeMdManager {
|
|
19
|
-
readonly resume: ResumeManager;
|
|
20
|
-
readonly generator: ClaudeMdGenerator;
|
|
21
|
-
readonly detector: TechDetector;
|
|
22
|
-
readonly persona: PersonaManager;
|
|
23
|
-
|
|
24
|
-
constructor(storage: SQLiteStorage, ai: ClaudeProvider) {
|
|
25
|
-
this.detector = new TechDetector();
|
|
26
|
-
this.resume = new ResumeManager(storage);
|
|
27
|
-
this.generator = new ClaudeMdGenerator(ai, this.detector);
|
|
28
|
-
this.persona = new PersonaManager();
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export { ResumeManager } from './resume-manager.js';
|
|
33
|
-
export { ClaudeMdGenerator } from './claudemd-generator.js';
|
|
34
|
-
export { TechDetector, type TechStack } from './tech-detector.js';
|
|
35
|
-
export { PersonaManager } from './persona-manager.js';
|