@winspan/claude-forge 8.51.1 → 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 +121 -2
- 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/constants.d.ts +2 -0
- package/dist/core/constants.d.ts.map +1 -1
- package/dist/core/constants.js +4 -0
- package/dist/core/constants.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 +30 -5
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/skill-sync.d.ts +21 -0
- package/dist/daemon/skill-sync.d.ts.map +1 -0
- package/dist/daemon/skill-sync.js +75 -0
- package/dist/daemon/skill-sync.js.map +1 -0
- package/dist/hooks/notification.sh +1 -1
- package/dist/hooks/post-tool-use.sh +1 -1
- package/dist/hooks/pre-tool-use.sh +1 -1
- package/dist/hooks/stop.sh +1 -1
- package/dist/hooks/user-prompt-submit.sh +1 -1
- package/dist/skills/official/code-simplifier.md +37 -1
- package/dist/skills/official/find-skills.md +120 -1
- package/dist/skills/official/official-api-design.md +14 -1
- package/dist/skills/official/official-architecture-decision.md +22 -1
- package/dist/skills/official/official-db-schema-design.md +19 -1
- package/dist/skills/official/official-debug.md +9 -1
- package/dist/skills/official/official-pr-review.md +1 -1
- package/dist/skills/official/official-security-hardening.md +7 -1
- package/dist/skills/official/planning-with-files.md +206 -2
- package/dist/skills/official/ui-ux-pro-max.md +88 -1
- package/dist/skills/official/webapp-testing.md +85 -1
- package/dist/skills/registry.d.ts +1 -1
- package/dist/skills/registry.d.ts.map +1 -1
- package/dist/skills/registry.js +15 -4
- package/dist/skills/registry.js.map +1 -1
- package/dist/skills/semantic-matcher.d.ts +4 -3
- package/dist/skills/semantic-matcher.d.ts.map +1 -1
- package/dist/skills/semantic-matcher.js +20 -22
- package/dist/skills/semantic-matcher.js.map +1 -1
- package/dist/skills/upgrade-engine.d.ts +93 -0
- package/dist/skills/upgrade-engine.d.ts.map +1 -0
- package/dist/skills/upgrade-engine.js +447 -0
- package/dist/skills/upgrade-engine.js.map +1 -0
- package/dist/skills/upgrade-prompt.d.ts +20 -0
- package/dist/skills/upgrade-prompt.d.ts.map +1 -0
- package/dist/skills/upgrade-prompt.js +75 -0
- package/dist/skills/upgrade-prompt.js.map +1 -0
- 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/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/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/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 -185
- 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 -62
- 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 -302
- 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/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 -16
- package/src/skills/official/find-skills.md +0 -23
- package/src/skills/official/official-api-design.md +0 -17
- package/src/skills/official/official-architecture-decision.md +0 -20
- package/src/skills/official/official-bmad.md +0 -118
- package/src/skills/official/official-db-schema-design.md +0 -16
- package/src/skills/official/official-debug.md +0 -17
- 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 -26
- package/src/skills/official/official-spec-driven-design.md +0 -31
- package/src/skills/official/planning-with-files.md +0 -37
- package/src/skills/official/ui-ux-pro-max.md +0 -18
- package/src/skills/official/webapp-testing.md +0 -12
- package/src/skills/official-skills.ts +0 -89
- package/src/skills/registry.ts +0 -355
- package/src/skills/semantic-matcher.ts +0 -231
- 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/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/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/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,63 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Claude Forge - PreToolUse Hook(双向通信)
|
|
3
|
-
|
|
4
|
-
# shellcheck source=./hook-lib.sh
|
|
5
|
-
source "$(dirname "$0")/hook-lib.sh"
|
|
6
|
-
|
|
7
|
-
AUTH_TOKEN=$(cat "$HOME/.claude-forge/daemon.token" 2>/dev/null || echo '')
|
|
8
|
-
|
|
9
|
-
# 读取 stdin(Claude Code 传入的 JSON)
|
|
10
|
-
INPUT=$(cat)
|
|
11
|
-
|
|
12
|
-
# 提取字段(单次 jq 调用,替代 3 个 python3 进程)
|
|
13
|
-
TOOL_NAME="${CLAUDE_TOOL_NAME:-$(echo "$INPUT" | jq -r '.tool_name // ""')}"
|
|
14
|
-
TOOL_INPUT=$(echo "$INPUT" | jq -c '.tool_input // {}')
|
|
15
|
-
RAW_CWD=$(echo "$INPUT" | jq -r '.cwd // ""')
|
|
16
|
-
PROJECT_PATH=$(resolve_project_path "${RAW_CWD:-$PWD}")
|
|
17
|
-
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // .sessionId // ""')
|
|
18
|
-
SESSION_ID="${SESSION_ID:-${CLAUDE_CODE_SESSION_ID:-cli}}"
|
|
19
|
-
|
|
20
|
-
# 构造事件 JSON(jq -n 替代 python3 heredoc)
|
|
21
|
-
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
|
|
22
|
-
EVENT=$(jq -n \
|
|
23
|
-
--arg hook_type "PreToolUse" \
|
|
24
|
-
--arg timestamp "$TIMESTAMP" \
|
|
25
|
-
--arg session_id "$SESSION_ID" \
|
|
26
|
-
--arg project_path "$PROJECT_PATH" \
|
|
27
|
-
--arg tool_name "$TOOL_NAME" \
|
|
28
|
-
--argjson tool_input "$TOOL_INPUT" \
|
|
29
|
-
--arg auth "$AUTH_TOKEN" \
|
|
30
|
-
'{hook_type: $hook_type, timestamp: $timestamp, session_id: $session_id,
|
|
31
|
-
project_path: $project_path, tool_name: $tool_name, tool_input: $tool_input,
|
|
32
|
-
_auth: $auth}')
|
|
33
|
-
|
|
34
|
-
# 发送事件(5 秒超时;失败时入队)
|
|
35
|
-
send_event_or_enqueue "$EVENT" 5
|
|
36
|
-
RESPONSE="$HOOK_RESPONSE"
|
|
37
|
-
|
|
38
|
-
if [ -n "$RESPONSE" ]; then
|
|
39
|
-
ALLOW=$(echo "$RESPONSE" | jq -r '.allow // true')
|
|
40
|
-
|
|
41
|
-
if [ "$ALLOW" = "false" ]; then
|
|
42
|
-
# 优先使用 hookSpecificOutput(Claude Code 官方格式),否则降级到 stderr + exit 2
|
|
43
|
-
HAS_HOOK_OUTPUT=$(echo "$RESPONSE" | jq -r 'if .hookSpecificOutput then "yes" else "no" end')
|
|
44
|
-
if [ "$HAS_HOOK_OUTPUT" = "yes" ]; then
|
|
45
|
-
# 输出 hookSpecificOutput 格式到 stdout,但必须 exit 非0 才能真正阻止工具执行
|
|
46
|
-
echo "$RESPONSE" | jq '{hookSpecificOutput: .hookSpecificOutput}'
|
|
47
|
-
exit 1
|
|
48
|
-
else
|
|
49
|
-
# 降级:输出原因到 stderr,exit 2 强制阻断
|
|
50
|
-
REASON=$(echo "$RESPONSE" | jq -r '.reason // "操作被 Forge 阻断"')
|
|
51
|
-
echo "$REASON" >&2
|
|
52
|
-
exit 2
|
|
53
|
-
fi
|
|
54
|
-
fi
|
|
55
|
-
|
|
56
|
-
# 有 additionalContext 时输出
|
|
57
|
-
HAS_CONTEXT=$(echo "$RESPONSE" | jq -r 'if .additionalContext and (.additionalContext != "") then "yes" else "no" end')
|
|
58
|
-
if [ "$HAS_CONTEXT" = "yes" ]; then
|
|
59
|
-
echo "$RESPONSE"
|
|
60
|
-
fi
|
|
61
|
-
fi
|
|
62
|
-
|
|
63
|
-
exit 0
|
package/src/hooks/stop.sh
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Claude Forge - Stop Hook
|
|
3
|
-
|
|
4
|
-
# shellcheck source=./hook-lib.sh
|
|
5
|
-
source "$(dirname "$0")/hook-lib.sh"
|
|
6
|
-
|
|
7
|
-
AUTH_TOKEN=$(cat "$HOME/.claude-forge/daemon.token" 2>/dev/null || echo '')
|
|
8
|
-
|
|
9
|
-
# 读取 stdin
|
|
10
|
-
INPUT=$(cat 2>/dev/null || echo '{}')
|
|
11
|
-
|
|
12
|
-
# 提取 cwd,上溯 git root
|
|
13
|
-
RAW_CWD=$(echo "$INPUT" | jq -r '.cwd // ""')
|
|
14
|
-
PROJECT_PATH=$(resolve_project_path "${RAW_CWD:-$PWD}")
|
|
15
|
-
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // .sessionId // ""')
|
|
16
|
-
SESSION_ID="${SESSION_ID:-${CLAUDE_CODE_SESSION_ID:-cli}}"
|
|
17
|
-
|
|
18
|
-
# 构造事件 JSON
|
|
19
|
-
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
|
|
20
|
-
EVENT=$(jq -n \
|
|
21
|
-
--arg hook_type "Stop" \
|
|
22
|
-
--arg timestamp "$TIMESTAMP" \
|
|
23
|
-
--arg session_id "$SESSION_ID" \
|
|
24
|
-
--arg project_path "$PROJECT_PATH" \
|
|
25
|
-
--arg auth "$AUTH_TOKEN" \
|
|
26
|
-
'{hook_type: $hook_type, timestamp: $timestamp, session_id: $session_id,
|
|
27
|
-
project_path: $project_path, tool_name: "stop", tool_input: {}, _auth: $auth}')
|
|
28
|
-
|
|
29
|
-
# 发送事件并读取响应(失败时入队)
|
|
30
|
-
send_event_or_enqueue "$EVENT" 5
|
|
31
|
-
RESPONSE="$HOOK_RESPONSE"
|
|
32
|
-
|
|
33
|
-
# 解析响应:allow=false 时输出 reason 并以 exit 2 阻断
|
|
34
|
-
if [ -n "$RESPONSE" ]; then
|
|
35
|
-
ALLOW=$(echo "$RESPONSE" | jq -r '.allow // true' 2>/dev/null)
|
|
36
|
-
if [ "$ALLOW" = "false" ]; then
|
|
37
|
-
REASON=$(echo "$RESPONSE" | jq -r '.reason // "质量门禁阻断"' 2>/dev/null)
|
|
38
|
-
echo "$REASON"
|
|
39
|
-
exit 2
|
|
40
|
-
fi
|
|
41
|
-
fi
|
|
42
|
-
|
|
43
|
-
exit 0
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Claude Forge - UserPromptSubmit Hook
|
|
3
|
-
# 拦截用户输入,触发意图分析和自动 Pipeline 编排
|
|
4
|
-
|
|
5
|
-
# shellcheck source=./hook-lib.sh
|
|
6
|
-
source "$(dirname "$0")/hook-lib.sh"
|
|
7
|
-
|
|
8
|
-
AUTH_TOKEN=$(cat "$HOME/.claude-forge/daemon.token" 2>/dev/null || echo '')
|
|
9
|
-
|
|
10
|
-
# 读取 stdin(Claude Code 传入的 JSON)
|
|
11
|
-
INPUT=$(cat)
|
|
12
|
-
|
|
13
|
-
# 提取字段(jq 替代 python3)
|
|
14
|
-
USER_PROMPT=$(echo "$INPUT" | jq -r '.prompt // ""')
|
|
15
|
-
RAW_CWD=$(echo "$INPUT" | jq -r '.cwd // ""')
|
|
16
|
-
PROJECT_PATH=$(resolve_project_path "${RAW_CWD:-$PWD}")
|
|
17
|
-
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // .sessionId // ""')
|
|
18
|
-
SESSION_ID="${SESSION_ID:-${CLAUDE_CODE_SESSION_ID:-cli}}"
|
|
19
|
-
|
|
20
|
-
# 空 prompt 跳过
|
|
21
|
-
if [ -z "$USER_PROMPT" ]; then
|
|
22
|
-
exit 0
|
|
23
|
-
fi
|
|
24
|
-
|
|
25
|
-
# 构造事件 JSON(jq -n 替代 python3 heredoc)
|
|
26
|
-
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
|
|
27
|
-
EVENT=$(jq -n \
|
|
28
|
-
--arg hook_type "UserPromptSubmit" \
|
|
29
|
-
--arg timestamp "$TIMESTAMP" \
|
|
30
|
-
--arg session_id "$SESSION_ID" \
|
|
31
|
-
--arg project_path "$PROJECT_PATH" \
|
|
32
|
-
--arg user_prompt "$USER_PROMPT" \
|
|
33
|
-
--arg auth "$AUTH_TOKEN" \
|
|
34
|
-
'{hook_type: $hook_type, timestamp: $timestamp, session_id: $session_id,
|
|
35
|
-
project_path: $project_path, tool_name: "UserPrompt",
|
|
36
|
-
user_prompt: $user_prompt, tool_input: {user_prompt: $user_prompt}, _auth: $auth}')
|
|
37
|
-
|
|
38
|
-
if [ -z "$EVENT" ]; then
|
|
39
|
-
exit 0
|
|
40
|
-
fi
|
|
41
|
-
|
|
42
|
-
# 发送事件(10 秒超时,AI 分析可能需要更多时间)
|
|
43
|
-
# 失败时自动入队(daemon 停机期间事件不丢失)
|
|
44
|
-
send_event_or_enqueue "$EVENT" 10
|
|
45
|
-
RESPONSE="$HOOK_RESPONSE"
|
|
46
|
-
|
|
47
|
-
if [ -n "$RESPONSE" ]; then
|
|
48
|
-
HAS_SYSTEM=$(echo "$RESPONSE" | jq -r 'if .systemMessage and (.systemMessage != "") then "yes" else "no" end')
|
|
49
|
-
HAS_CONTEXT=$(echo "$RESPONSE" | jq -r 'if .additionalContext and (.additionalContext != "") then "yes" else "no" end')
|
|
50
|
-
|
|
51
|
-
if [ "$HAS_SYSTEM" = "yes" ] || [ "$HAS_CONTEXT" = "yes" ]; then
|
|
52
|
-
# stderr 输出摘要到终端
|
|
53
|
-
if [ "$HAS_SYSTEM" = "yes" ]; then
|
|
54
|
-
SM_LEN=$(echo "$RESPONSE" | jq -r '.systemMessage | length')
|
|
55
|
-
printf '\033[36m💉 [Forge] 注入 systemMessage (%s chars)\033[0m\n' "$SM_LEN" >&2
|
|
56
|
-
fi
|
|
57
|
-
if [ "$HAS_CONTEXT" = "yes" ]; then
|
|
58
|
-
CONTEXT=$(echo "$RESPONSE" | jq -r '.additionalContext // ""')
|
|
59
|
-
DISPLAY_LINES=$(printf '%s' "$CONTEXT" | head -3)
|
|
60
|
-
if [ -n "$DISPLAY_LINES" ]; then
|
|
61
|
-
printf '\033[36m💉 [Forge] %s\033[0m\n' "$DISPLAY_LINES" >&2
|
|
62
|
-
fi
|
|
63
|
-
fi
|
|
64
|
-
# stdout 返回给 Claude Code
|
|
65
|
-
echo "$RESPONSE"
|
|
66
|
-
fi
|
|
67
|
-
fi
|
|
68
|
-
|
|
69
|
-
exit 0
|
package/src/mcp/server.ts
DELETED
|
@@ -1,322 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP Server for claude-forge
|
|
3
|
-
*
|
|
4
|
-
* 嵌入在 daemon 进程内,通过 HTTP(Streamable HTTP transport)向 Claude Code
|
|
5
|
-
* 暴露以下工具:
|
|
6
|
-
* - skill_invoke:Agent 按需获取某个 Skill 的方法论内容
|
|
7
|
-
* - skill_list:列出所有可用 Skill(id / name / description / keywords)
|
|
8
|
-
*
|
|
9
|
-
* 调用链路:
|
|
10
|
-
* Claude Code → HTTP POST /mcp → MCP server → skillInvoke()/skillList()
|
|
11
|
-
* → writeSkillInvocation() 记录
|
|
12
|
-
*
|
|
13
|
-
* 设计要点:
|
|
14
|
-
* - 复用现有 Express app(不新增端口),路径前缀固定为 /mcp
|
|
15
|
-
* - 沿用 daemon auth token(Bearer),与 /api 写操作同一鉴权链路
|
|
16
|
-
* - Stateless 模式:每个请求开一对 server/transport,避免长连接污染
|
|
17
|
-
* daemon 主循环;Skill 调用本身是短平快、无需流式推送的场景
|
|
18
|
-
* - Session 追踪:优先 header `X-Forge-Session-Id`,缺失时生成匿名 session
|
|
19
|
-
* (Ruflo 模式下 Claude 自主决策,不需要 routing state 回退)
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
import type { Application, Request, Response } from 'express';
|
|
23
|
-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
24
|
-
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
25
|
-
import { z } from 'zod';
|
|
26
|
-
|
|
27
|
-
import type { SkillRegistry } from '../skills/registry.js';
|
|
28
|
-
import type { SQLiteStorage } from '../core/storage/sqlite.js';
|
|
29
|
-
import type { InvocationGuard } from '../skills/invocation-guard.js';
|
|
30
|
-
import { skillInvoke } from '../skills/tools/skill-invoke.js';
|
|
31
|
-
import { skillList } from '../skills/tools/skill-list.js';
|
|
32
|
-
import { pipelineSuggest } from '../skills/tools/pipeline-suggest.js';
|
|
33
|
-
import { logger } from '../core/utils/logger.js';
|
|
34
|
-
import { requireAuth } from '../web/auth-middleware.js';
|
|
35
|
-
|
|
36
|
-
/** Resolved session context for a single MCP tool call. */
|
|
37
|
-
export interface SessionContext {
|
|
38
|
-
sessionId: string | null;
|
|
39
|
-
routeRequestId: string | null;
|
|
40
|
-
agentId: string | null;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface McpServerOptions {
|
|
44
|
-
skillRegistry: SkillRegistry;
|
|
45
|
-
storage: SQLiteStorage;
|
|
46
|
-
guard?: InvocationGuard;
|
|
47
|
-
/**
|
|
48
|
-
* Override the session-resolution strategy. Default behavior:
|
|
49
|
-
* 1. Read header `X-Forge-Session-Id` (passed by the harness if available)
|
|
50
|
-
* 2. Fallback: pick the freshest live routing entry from routingState
|
|
51
|
-
* 3. If neither yields a sessionId → return all-null context (invocation
|
|
52
|
-
* will be recorded with session_id derived from the synthetic
|
|
53
|
-
* "mcp-anon-<ts>" so the writeSkillInvocation NOT NULL constraint still
|
|
54
|
-
* passes; see resolveSessionContext below).
|
|
55
|
-
*/
|
|
56
|
-
resolveSession?: (req: Request) => SessionContext;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const SERVER_INFO = {
|
|
60
|
-
name: 'claude-forge',
|
|
61
|
-
version: '1.0.0',
|
|
62
|
-
} as const;
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Resolve session context for a request.
|
|
66
|
-
*
|
|
67
|
-
* Priority order:
|
|
68
|
-
* 1. Explicit header `X-Forge-Session-Id` (+ optional `X-Forge-Route-Request-Id`,
|
|
69
|
-
* `X-Forge-Agent-Id`).
|
|
70
|
-
* 2. routingState.getMostRecent() — heuristic fallback during the routing
|
|
71
|
-
* window (≤30min TTL).
|
|
72
|
-
* 3. Synthetic anonymous session "mcp-anon-<unix-ts>" so we never blow the
|
|
73
|
-
* NOT NULL session_id constraint when persisting invocations.
|
|
74
|
-
*/
|
|
75
|
-
function resolveSessionContext(req: Request): SessionContext {
|
|
76
|
-
const headerSession = req.header('X-Forge-Session-Id');
|
|
77
|
-
if (headerSession && headerSession.trim().length > 0) {
|
|
78
|
-
const headerRoute = req.header('X-Forge-Route-Request-Id');
|
|
79
|
-
const headerAgent = req.header('X-Forge-Agent-Id');
|
|
80
|
-
return {
|
|
81
|
-
sessionId: headerSession.trim(),
|
|
82
|
-
routeRequestId: headerRoute?.trim() || null,
|
|
83
|
-
agentId: headerAgent?.trim() || null,
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// In Ruflo mode, Claude makes autonomous decisions via CLAUDE.md.
|
|
88
|
-
// MCP tools no longer need routing state fallback.
|
|
89
|
-
return {
|
|
90
|
-
sessionId: `mcp-anon-${Date.now()}`,
|
|
91
|
-
routeRequestId: null,
|
|
92
|
-
agentId: null,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/** Build a fresh MCP server instance bound to the given options & context. */
|
|
97
|
-
function buildServer(
|
|
98
|
-
options: McpServerOptions,
|
|
99
|
-
sessionCtx: SessionContext,
|
|
100
|
-
): McpServer {
|
|
101
|
-
const server = new McpServer(SERVER_INFO, {
|
|
102
|
-
capabilities: { tools: { listChanged: false } },
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
// ── Tool 1: skill_invoke ───────────────────────────────────────────────
|
|
106
|
-
server.registerTool(
|
|
107
|
-
'skill_invoke',
|
|
108
|
-
{
|
|
109
|
-
title: 'Invoke a claude-forge skill',
|
|
110
|
-
description:
|
|
111
|
-
'调用指定的 Skill,获取该方法论的完整内容。使用场景:当你需要特定领域的方法论指导(如 TDD、重构、性能优化)时。',
|
|
112
|
-
inputSchema: {
|
|
113
|
-
skill_id: z
|
|
114
|
-
.string()
|
|
115
|
-
.min(1)
|
|
116
|
-
.describe('Skill 的 ID(如 "official-tdd")。可通过 skill_list 工具查询。'),
|
|
117
|
-
reason: z
|
|
118
|
-
.string()
|
|
119
|
-
.optional()
|
|
120
|
-
.describe('调用理由(可选,用于审计 / skill_invocations.reason 列)'),
|
|
121
|
-
},
|
|
122
|
-
},
|
|
123
|
-
async (args) => {
|
|
124
|
-
try {
|
|
125
|
-
const sessionId = sessionCtx.sessionId ?? `mcp-anon-${Date.now()}`;
|
|
126
|
-
const result = await skillInvoke(
|
|
127
|
-
{ skill_id: args.skill_id, reason: args.reason },
|
|
128
|
-
{
|
|
129
|
-
skillRegistry: options.skillRegistry,
|
|
130
|
-
guard: options.guard,
|
|
131
|
-
storage: options.storage,
|
|
132
|
-
sessionId,
|
|
133
|
-
routeRequestId: sessionCtx.routeRequestId ?? undefined,
|
|
134
|
-
agentId: sessionCtx.agentId ?? undefined,
|
|
135
|
-
invocationType: 'dynamic',
|
|
136
|
-
},
|
|
137
|
-
);
|
|
138
|
-
return {
|
|
139
|
-
content: [
|
|
140
|
-
{
|
|
141
|
-
type: 'text' as const,
|
|
142
|
-
text: `# ${result.skill_name}\n\n${result.content}`,
|
|
143
|
-
},
|
|
144
|
-
],
|
|
145
|
-
};
|
|
146
|
-
} catch (err) {
|
|
147
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
148
|
-
logger.warn(`[MCP] skill_invoke failed: ${message}`);
|
|
149
|
-
return {
|
|
150
|
-
isError: true,
|
|
151
|
-
content: [{ type: 'text' as const, text: message }],
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
},
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
// ── Tool 2: skill_list ─────────────────────────────────────────────────
|
|
158
|
-
server.registerTool(
|
|
159
|
-
'skill_list',
|
|
160
|
-
{
|
|
161
|
-
title: 'List available claude-forge skills',
|
|
162
|
-
description: '列出所有可用的 Skill。可以通过 keywords 过滤(OR 语义)。',
|
|
163
|
-
inputSchema: {
|
|
164
|
-
keywords: z
|
|
165
|
-
.array(z.string())
|
|
166
|
-
.optional()
|
|
167
|
-
.describe('可选的关键词过滤(如 ["test", "tdd"])'),
|
|
168
|
-
},
|
|
169
|
-
},
|
|
170
|
-
async (args) => {
|
|
171
|
-
try {
|
|
172
|
-
logger.info(`[MCP] skill_list called with args: ${JSON.stringify(args)}`);
|
|
173
|
-
const result = await skillList(
|
|
174
|
-
{ keywords: args.keywords },
|
|
175
|
-
{ skillRegistry: options.skillRegistry },
|
|
176
|
-
);
|
|
177
|
-
return {
|
|
178
|
-
content: [
|
|
179
|
-
{ type: 'text' as const, text: JSON.stringify(result, null, 2) },
|
|
180
|
-
],
|
|
181
|
-
};
|
|
182
|
-
} catch (err) {
|
|
183
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
184
|
-
logger.warn(`[MCP] skill_list failed: ${message}`);
|
|
185
|
-
return {
|
|
186
|
-
isError: true,
|
|
187
|
-
content: [{ type: 'text' as const, text: message }],
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
},
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
// ── Tool 3: pipeline_suggest ───────────────────────────────────────────
|
|
194
|
-
server.registerTool(
|
|
195
|
-
'pipeline_suggest',
|
|
196
|
-
{
|
|
197
|
-
title: 'Suggest a swarm pipeline for a task',
|
|
198
|
-
description:
|
|
199
|
-
'根据任务特征判断是否需要启动多 Agent 协作的 Pipeline,并生成可直接使用的 Task({...}) 调用代码。',
|
|
200
|
-
inputSchema: {
|
|
201
|
-
task_description: z
|
|
202
|
-
.string()
|
|
203
|
-
.min(1)
|
|
204
|
-
.describe('任务描述(如 "修复用户登录时的 session 过期问题")'),
|
|
205
|
-
file_count: z
|
|
206
|
-
.number()
|
|
207
|
-
.int()
|
|
208
|
-
.optional()
|
|
209
|
-
.describe('预估涉及的文件数量(>=2 时建议使用 Pipeline)'),
|
|
210
|
-
is_new_feature: z
|
|
211
|
-
.boolean()
|
|
212
|
-
.optional()
|
|
213
|
-
.describe('是否为新功能开发'),
|
|
214
|
-
is_refactor: z
|
|
215
|
-
.boolean()
|
|
216
|
-
.optional()
|
|
217
|
-
.describe('是否为重构任务'),
|
|
218
|
-
is_bug_fix: z
|
|
219
|
-
.boolean()
|
|
220
|
-
.optional()
|
|
221
|
-
.describe('是否为 Bug 修复'),
|
|
222
|
-
is_performance: z
|
|
223
|
-
.boolean()
|
|
224
|
-
.optional()
|
|
225
|
-
.describe('是否为性能优化'),
|
|
226
|
-
is_migration: z
|
|
227
|
-
.boolean()
|
|
228
|
-
.optional()
|
|
229
|
-
.describe('是否为技术迁移/升级'),
|
|
230
|
-
is_security: z
|
|
231
|
-
.boolean()
|
|
232
|
-
.optional()
|
|
233
|
-
.describe('是否为安全审计/漏洞修复'),
|
|
234
|
-
},
|
|
235
|
-
},
|
|
236
|
-
async (args) => {
|
|
237
|
-
try {
|
|
238
|
-
const result = await pipelineSuggest({
|
|
239
|
-
task_description: args.task_description,
|
|
240
|
-
file_count: args.file_count,
|
|
241
|
-
is_new_feature: args.is_new_feature,
|
|
242
|
-
is_refactor: args.is_refactor,
|
|
243
|
-
is_bug_fix: args.is_bug_fix,
|
|
244
|
-
is_performance: args.is_performance,
|
|
245
|
-
is_migration: args.is_migration,
|
|
246
|
-
is_security: args.is_security,
|
|
247
|
-
});
|
|
248
|
-
return {
|
|
249
|
-
content: [
|
|
250
|
-
{ type: 'text' as const, text: JSON.stringify(result, null, 2) },
|
|
251
|
-
],
|
|
252
|
-
};
|
|
253
|
-
} catch (err) {
|
|
254
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
255
|
-
logger.warn(`[MCP] pipeline_suggest failed: ${message}`);
|
|
256
|
-
return {
|
|
257
|
-
isError: true,
|
|
258
|
-
content: [{ type: 'text' as const, text: message }],
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
},
|
|
262
|
-
);
|
|
263
|
-
|
|
264
|
-
return server;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Mount the MCP server endpoints on an existing Express application.
|
|
269
|
-
*
|
|
270
|
-
* Routes:
|
|
271
|
-
* POST /mcp — JSON-RPC over Streamable HTTP (initialize, tools/list, tools/call)
|
|
272
|
-
* GET /mcp — Standalone SSE channel (server→client notifications)
|
|
273
|
-
* DELETE /mcp — Session termination (stateful mode only; no-op in stateless)
|
|
274
|
-
*
|
|
275
|
-
* Authentication: every method requires the daemon Bearer token. Token-file
|
|
276
|
-
* absence still falls through (early-startup safety, identical to /api).
|
|
277
|
-
*/
|
|
278
|
-
export function mountMcpServer(app: Application, options: McpServerOptions): void {
|
|
279
|
-
const resolveSession = options.resolveSession ?? resolveSessionContext;
|
|
280
|
-
|
|
281
|
-
const handler = async (req: Request, res: Response): Promise<void> => {
|
|
282
|
-
// Stateless: build a fresh server+transport per request. The transport
|
|
283
|
-
// mutates res so it cannot be reused across requests anyway.
|
|
284
|
-
const sessionCtx = resolveSession(req);
|
|
285
|
-
const server = buildServer(options, sessionCtx);
|
|
286
|
-
const transport = new StreamableHTTPServerTransport({
|
|
287
|
-
// Stateless mode — no session ID generator.
|
|
288
|
-
sessionIdGenerator: undefined,
|
|
289
|
-
enableJsonResponse: true,
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
res.on('close', () => {
|
|
293
|
-
// best-effort cleanup; ignore errors because transport may already be closed
|
|
294
|
-
void transport.close().catch(() => undefined);
|
|
295
|
-
void server.close().catch(() => undefined);
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
try {
|
|
299
|
-
await server.connect(transport);
|
|
300
|
-
// express.json() has already populated req.body; pass it through.
|
|
301
|
-
await transport.handleRequest(req, res, req.body);
|
|
302
|
-
} catch (err) {
|
|
303
|
-
logger.warn(`[MCP] request failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
304
|
-
if (!res.headersSent) {
|
|
305
|
-
res.status(500).json({
|
|
306
|
-
jsonrpc: '2.0',
|
|
307
|
-
error: { code: -32603, message: 'Internal error' },
|
|
308
|
-
id: null,
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
// All MCP requests are auth-gated. We deliberately gate GET too because MCP
|
|
315
|
-
// tool-calls (sensitive ops) can come over GET via SSE; /api keeps GET open
|
|
316
|
-
// only for read-only dashboard polling.
|
|
317
|
-
app.post('/mcp', requireAuth, handler);
|
|
318
|
-
app.get('/mcp', requireAuth, handler);
|
|
319
|
-
app.delete('/mcp', requireAuth, handler);
|
|
320
|
-
|
|
321
|
-
logger.info('[MCP] Server mounted on /mcp (Streamable HTTP, stateless)');
|
|
322
|
-
}
|
package/src/skills/index.ts
DELETED
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* InvocationGuard — Skill 调用防护机制
|
|
3
|
-
*
|
|
4
|
-
* 防止 Agent 陷入无限调用 Skill 的循环,限制:
|
|
5
|
-
* - 单会话总调用次数(默认 10)
|
|
6
|
-
* - 单会话调用深度(默认 3)
|
|
7
|
-
* - 同 session 同 skill 幂等(避免重复调用)
|
|
8
|
-
* - TTL 机制:会话状态 30 分钟后自动过期
|
|
9
|
-
*
|
|
10
|
-
* 状态为进程内存,进程重启会清空;调用链路通常集中在单个会话内,
|
|
11
|
-
* 重启后重建状态是可接受的。
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
const MAX_INVOCATIONS_PER_SESSION = 10;
|
|
15
|
-
const MAX_INVOCATION_DEPTH = 3;
|
|
16
|
-
const SESSION_TTL_MS = 30 * 60 * 1000; // 30 minutes
|
|
17
|
-
const CLEANUP_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
|
|
18
|
-
|
|
19
|
-
export interface SessionInvocations {
|
|
20
|
-
total: number;
|
|
21
|
-
depth: number;
|
|
22
|
-
calledSkills: Set<string>;
|
|
23
|
-
lastAccessTime: number; // Timestamp of last access
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface InvocationCheckResult {
|
|
27
|
-
allowed: boolean;
|
|
28
|
-
reason?: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export class InvocationGuard {
|
|
32
|
-
private sessions = new Map<string, SessionInvocations>();
|
|
33
|
-
private cleanupTimer: NodeJS.Timeout | null = null;
|
|
34
|
-
|
|
35
|
-
constructor() {
|
|
36
|
-
// Start automatic cleanup timer
|
|
37
|
-
this.startCleanupTimer();
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* 检查是否允许调用。
|
|
42
|
-
* 不修改状态;调用方决定是否继续后调用 record()。
|
|
43
|
-
*/
|
|
44
|
-
check(sessionId: string, skillId: string): InvocationCheckResult {
|
|
45
|
-
const session = this.sessions.get(sessionId) ?? {
|
|
46
|
-
total: 0,
|
|
47
|
-
depth: 0,
|
|
48
|
-
calledSkills: new Set<string>(),
|
|
49
|
-
lastAccessTime: Date.now(),
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
// Check if session has expired
|
|
53
|
-
if (Date.now() - session.lastAccessTime > SESSION_TTL_MS) {
|
|
54
|
-
this.sessions.delete(sessionId);
|
|
55
|
-
return { allowed: true }; // Expired session, allow new invocation
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (session.total >= MAX_INVOCATIONS_PER_SESSION) {
|
|
59
|
-
return {
|
|
60
|
-
allowed: false,
|
|
61
|
-
reason: `Reached max invocations per session (${MAX_INVOCATIONS_PER_SESSION})`,
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (session.depth >= MAX_INVOCATION_DEPTH) {
|
|
66
|
-
return {
|
|
67
|
-
allowed: false,
|
|
68
|
-
reason: `Reached max invocation depth (${MAX_INVOCATION_DEPTH})`,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (session.calledSkills.has(skillId)) {
|
|
73
|
-
return {
|
|
74
|
-
allowed: false,
|
|
75
|
-
reason: `Skill '${skillId}' already invoked in this session (idempotent guard)`,
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return { allowed: true };
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* 记录一次调用开始(递增 total 和 depth,登记 skill)。
|
|
84
|
-
*/
|
|
85
|
-
record(sessionId: string, skillId: string): void {
|
|
86
|
-
const session = this.sessions.get(sessionId) ?? {
|
|
87
|
-
total: 0,
|
|
88
|
-
depth: 0,
|
|
89
|
-
calledSkills: new Set<string>(),
|
|
90
|
-
lastAccessTime: Date.now(),
|
|
91
|
-
};
|
|
92
|
-
session.total += 1;
|
|
93
|
-
session.depth += 1;
|
|
94
|
-
session.calledSkills.add(skillId);
|
|
95
|
-
session.lastAccessTime = Date.now(); // Update access time
|
|
96
|
-
this.sessions.set(sessionId, session);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* 调用完成,递减 depth(不清除已调用 skills)。
|
|
101
|
-
*/
|
|
102
|
-
complete(sessionId: string): void {
|
|
103
|
-
const session = this.sessions.get(sessionId);
|
|
104
|
-
if (session) {
|
|
105
|
-
session.depth = Math.max(0, session.depth - 1);
|
|
106
|
-
session.lastAccessTime = Date.now(); // Update access time
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* 清理会话(Stop hook 或会话结束时调用)。
|
|
112
|
-
*/
|
|
113
|
-
clear(sessionId: string): void {
|
|
114
|
-
this.sessions.delete(sessionId);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* 获取会话统计(调试/观测用)。
|
|
119
|
-
*/
|
|
120
|
-
getStats(sessionId: string): SessionInvocations | null {
|
|
121
|
-
return this.sessions.get(sessionId) ?? null;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* 清理过期会话(TTL 机制)。
|
|
126
|
-
*/
|
|
127
|
-
private cleanupExpiredSessions(): void {
|
|
128
|
-
const now = Date.now();
|
|
129
|
-
const expiredSessions: string[] = [];
|
|
130
|
-
|
|
131
|
-
for (const [sessionId, session] of this.sessions.entries()) {
|
|
132
|
-
if (now - session.lastAccessTime > SESSION_TTL_MS) {
|
|
133
|
-
expiredSessions.push(sessionId);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
for (const sessionId of expiredSessions) {
|
|
138
|
-
this.sessions.delete(sessionId);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (expiredSessions.length > 0) {
|
|
142
|
-
// Use console.log instead of logger to avoid circular dependency
|
|
143
|
-
console.log(`[InvocationGuard] Cleaned up ${expiredSessions.length} expired sessions`);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* 启动自动清理定时器。
|
|
149
|
-
*/
|
|
150
|
-
private startCleanupTimer(): void {
|
|
151
|
-
if (this.cleanupTimer) return;
|
|
152
|
-
|
|
153
|
-
this.cleanupTimer = setInterval(() => {
|
|
154
|
-
this.cleanupExpiredSessions();
|
|
155
|
-
}, CLEANUP_INTERVAL_MS);
|
|
156
|
-
|
|
157
|
-
// Prevent timer from keeping process alive
|
|
158
|
-
this.cleanupTimer.unref();
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* 停止自动清理定时器(用于测试或进程关闭)。
|
|
163
|
-
*/
|
|
164
|
-
stopCleanupTimer(): void {
|
|
165
|
-
if (this.cleanupTimer) {
|
|
166
|
-
clearInterval(this.cleanupTimer);
|
|
167
|
-
this.cleanupTimer = null;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* 获取当前会话数量(用于监控)。
|
|
173
|
-
*/
|
|
174
|
-
getSessionCount(): number {
|
|
175
|
-
return this.sessions.size;
|
|
176
|
-
}
|
|
177
|
-
}
|