@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
package/src/core/event-fields.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Typed getters for ForgeEvent.tool_input fields.
|
|
3
|
-
*
|
|
4
|
-
* Centralises the defensive-access pattern that previously lived as
|
|
5
|
-
* `(e.tool_input as any)?.command ?? ''` across 30+ call sites. Each
|
|
6
|
-
* getter returns `string | undefined`; callers decide their own fallback.
|
|
7
|
-
*
|
|
8
|
-
* Adding a new tool field? Extend ToolInputFields in core/types.ts first,
|
|
9
|
-
* then add a getter here if it needs a normalised access entry.
|
|
10
|
-
*/
|
|
11
|
-
import type { ForgeEvent } from './types.js';
|
|
12
|
-
|
|
13
|
-
export function getCommand(e: ForgeEvent): string | undefined {
|
|
14
|
-
const c = e.tool_input?.command;
|
|
15
|
-
return typeof c === 'string' ? c : undefined;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function getFilePath(e: ForgeEvent): string | undefined {
|
|
19
|
-
const fp = e.tool_input?.file_path ?? e.tool_input?.notebook_path;
|
|
20
|
-
return typeof fp === 'string' && fp.length > 0 ? fp : undefined;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function getUserPrompt(e: ForgeEvent): string | undefined {
|
|
24
|
-
if (typeof e.user_prompt === 'string') return e.user_prompt;
|
|
25
|
-
const fallback = e.tool_input?.user_prompt;
|
|
26
|
-
return typeof fallback === 'string' ? fallback : undefined;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function getSubagentType(e: ForgeEvent): string | undefined {
|
|
30
|
-
const s = e.tool_input?.subagent_type;
|
|
31
|
-
return typeof s === 'string' ? s : undefined;
|
|
32
|
-
}
|
package/src/core/queue/index.ts
DELETED
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Hook failure buffer queue — at-least-once event delivery
|
|
3
|
-
*
|
|
4
|
-
* When the daemon is not reachable (socket missing / nc fail), hook scripts
|
|
5
|
-
* write the raw event JSON to a per-file queue on disk. On the next daemon
|
|
6
|
-
* startup, `replayQueue` scans the directory and replays every file into
|
|
7
|
-
* SQLiteStorage.
|
|
8
|
-
*
|
|
9
|
-
* Design decisions:
|
|
10
|
-
* - One file per event: avoids concurrent-write collisions across 5 hook processes
|
|
11
|
-
* - File name: <iso-timestamp>-<event_id>.json (sortable + unique)
|
|
12
|
-
* - Dedup: events table PRIMARY KEY; UNIQUE constraint error → silently skip
|
|
13
|
-
* - Capacity: MAX_FILES=500, TTL=7d; overflow → drop oldest; expired → dead-letter
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import { mkdirSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
17
|
-
import { readFile } from 'node:fs/promises';
|
|
18
|
-
import { join } from 'node:path';
|
|
19
|
-
import { randomUUID } from 'node:crypto';
|
|
20
|
-
import type { SQLiteStorage } from '../storage/sqlite.js';
|
|
21
|
-
import type { ForgeEvent } from '../types.js';
|
|
22
|
-
import { logger } from '../utils/logger.js';
|
|
23
|
-
import { FORGE_PATHS } from '../constants.js';
|
|
24
|
-
|
|
25
|
-
// ── Constants ──────────────────────────────────────────────────────────────
|
|
26
|
-
export const QUEUE_DIR = FORGE_PATHS.queue();
|
|
27
|
-
export const DEAD_DIR = FORGE_PATHS.queueDead();
|
|
28
|
-
export const MAX_FILES = 500;
|
|
29
|
-
export const TTL_DAYS = 7;
|
|
30
|
-
|
|
31
|
-
const TTL_MS = TTL_DAYS * 24 * 60 * 60 * 1000;
|
|
32
|
-
|
|
33
|
-
// ── Helpers ────────────────────────────────────────────────────────────────
|
|
34
|
-
|
|
35
|
-
function ensureDirs(): void {
|
|
36
|
-
mkdirSync(QUEUE_DIR, { recursive: true });
|
|
37
|
-
mkdirSync(DEAD_DIR, { recursive: true });
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/** List all *.json queue files, sorted oldest-first (by filename). */
|
|
41
|
-
function listQueueFiles(): string[] {
|
|
42
|
-
try {
|
|
43
|
-
return readdirSync(QUEUE_DIR)
|
|
44
|
-
.filter((f) => f.endsWith('.json'))
|
|
45
|
-
.sort(); // ISO timestamp prefix → lexicographic = chronological
|
|
46
|
-
} catch {
|
|
47
|
-
return [];
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* pruneQueue — enforce MAX_FILES cap.
|
|
53
|
-
* Deletes the oldest files (smallest filename) until count < MAX_FILES.
|
|
54
|
-
* Called by enqueueEvent before writing a new file.
|
|
55
|
-
*/
|
|
56
|
-
export function pruneQueue(): void {
|
|
57
|
-
const files = listQueueFiles();
|
|
58
|
-
if (files.length < MAX_FILES) return;
|
|
59
|
-
|
|
60
|
-
const toDelete = files.slice(0, files.length - MAX_FILES + 1);
|
|
61
|
-
for (const f of toDelete) {
|
|
62
|
-
try {
|
|
63
|
-
rmSync(join(QUEUE_DIR, f));
|
|
64
|
-
} catch (err) {
|
|
65
|
-
logger.warn(`[Queue] Failed to prune ${f}: ${err}`);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// ── Public API ─────────────────────────────────────────────────────────────
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* enqueueEvent — write a single event to the queue directory.
|
|
74
|
-
*
|
|
75
|
-
* Designed to be called synchronously from hook scripts via a bash child
|
|
76
|
-
* process. This TypeScript version is used by tests; the bash implementation
|
|
77
|
-
* mirrors the same file-naming convention.
|
|
78
|
-
*/
|
|
79
|
-
export function enqueueEvent(event: ForgeEvent): void {
|
|
80
|
-
ensureDirs();
|
|
81
|
-
pruneQueue();
|
|
82
|
-
|
|
83
|
-
const eventId = event.event_id ?? randomUUID();
|
|
84
|
-
const ts = new Date().toISOString().replace(/[:.]/g, '-').replace('Z', 'Z');
|
|
85
|
-
const filename = `${ts}-${eventId}.json`;
|
|
86
|
-
const filePath = join(QUEUE_DIR, filename);
|
|
87
|
-
|
|
88
|
-
writeFileSync(filePath, JSON.stringify({ ...event, event_id: eventId }), 'utf8');
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export interface ReplayResult {
|
|
92
|
-
replayed: number;
|
|
93
|
-
skipped: number;
|
|
94
|
-
dead: number;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* replayQueue — called by daemon after socket starts listening.
|
|
99
|
-
*
|
|
100
|
-
* For each *.json in QUEUE_DIR:
|
|
101
|
-
* - If mtime > TTL → move to dead-letter
|
|
102
|
-
* - Parse JSON → storage.writeEvent()
|
|
103
|
-
* - Success → rm file, replayed++
|
|
104
|
-
* - UNIQUE constraint → rm file, skipped++ (already in DB)
|
|
105
|
-
* - Other error → move to dead-letter, dead++
|
|
106
|
-
* - Bad JSON → move to dead-letter, dead++
|
|
107
|
-
*/
|
|
108
|
-
export async function replayQueue(storage: SQLiteStorage): Promise<ReplayResult> {
|
|
109
|
-
ensureDirs();
|
|
110
|
-
|
|
111
|
-
const files = listQueueFiles();
|
|
112
|
-
if (files.length === 0) {
|
|
113
|
-
return { replayed: 0, skipped: 0, dead: 0 };
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
logger.info(`[Queue] Replaying ${files.length} queued event(s)…`);
|
|
117
|
-
|
|
118
|
-
let replayed = 0;
|
|
119
|
-
let skipped = 0;
|
|
120
|
-
let dead = 0;
|
|
121
|
-
const now = Date.now();
|
|
122
|
-
|
|
123
|
-
for (const filename of files) {
|
|
124
|
-
const filePath = join(QUEUE_DIR, filename);
|
|
125
|
-
|
|
126
|
-
// ── TTL check ──────────────────────────────────────────────────────────
|
|
127
|
-
try {
|
|
128
|
-
const stat = statSync(filePath);
|
|
129
|
-
if (now - stat.mtimeMs > TTL_MS) {
|
|
130
|
-
moveToDeadLetter(filePath, filename);
|
|
131
|
-
dead++;
|
|
132
|
-
logger.warn(`[Queue] TTL expired, dead-lettered: ${filename}`);
|
|
133
|
-
continue;
|
|
134
|
-
}
|
|
135
|
-
} catch (err) {
|
|
136
|
-
logger.warn(`[Queue] Cannot stat ${filename}, skipping: ${err}`);
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// ── Read + parse ───────────────────────────────────────────────────────
|
|
141
|
-
let event: ForgeEvent;
|
|
142
|
-
try {
|
|
143
|
-
const raw = await readFile(filePath, 'utf8');
|
|
144
|
-
event = JSON.parse(raw) as ForgeEvent;
|
|
145
|
-
} catch {
|
|
146
|
-
moveToDeadLetter(filePath, filename);
|
|
147
|
-
dead++;
|
|
148
|
-
logger.warn(`[Queue] Bad JSON, dead-lettered: ${filename}`);
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// ── Write to storage ───────────────────────────────────────────────────
|
|
153
|
-
try {
|
|
154
|
-
storage.writeEvent(event);
|
|
155
|
-
rmSync(filePath);
|
|
156
|
-
replayed++;
|
|
157
|
-
} catch (err) {
|
|
158
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
159
|
-
if (isUniqueConstraintError(msg)) {
|
|
160
|
-
// Already in DB — remove the queue file silently
|
|
161
|
-
try { rmSync(filePath); } catch { /* ignore */ }
|
|
162
|
-
skipped++;
|
|
163
|
-
} else {
|
|
164
|
-
moveToDeadLetter(filePath, filename);
|
|
165
|
-
dead++;
|
|
166
|
-
logger.warn(`[Queue] writeEvent failed, dead-lettered ${filename}: ${msg}`);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
return { replayed, skipped, dead };
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// ── Internal helpers ───────────────────────────────────────────────────────
|
|
175
|
-
|
|
176
|
-
function moveToDeadLetter(filePath: string, filename: string): void {
|
|
177
|
-
try {
|
|
178
|
-
mkdirSync(DEAD_DIR, { recursive: true });
|
|
179
|
-
renameSync(filePath, join(DEAD_DIR, filename));
|
|
180
|
-
} catch (err) {
|
|
181
|
-
logger.warn(`[Queue] Failed to move ${filename} to dead-letter: ${err}`);
|
|
182
|
-
try { rmSync(filePath); } catch { /* ignore */ }
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Detect SQLite UNIQUE constraint violations.
|
|
188
|
-
* better-sqlite3 throws: "UNIQUE constraint failed: events.event_id"
|
|
189
|
-
*/
|
|
190
|
-
function isUniqueConstraintError(message: string): boolean {
|
|
191
|
-
return message.includes('UNIQUE constraint failed');
|
|
192
|
-
}
|
package/src/core/storage/base.ts
DELETED
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SQLiteBase — 数据库初始化、schema、迁移、辅助
|
|
3
|
-
*
|
|
4
|
-
* 拆分自 sqlite.ts。仅负责:
|
|
5
|
-
* - 打开数据库连接 + 配置 PRAGMA
|
|
6
|
-
* - 加载 schema.sql(缺失则抛错)
|
|
7
|
-
* - 增量迁移(hasColumn / addColumnIfMissing)
|
|
8
|
-
* - 一次性 sessions 回填
|
|
9
|
-
*
|
|
10
|
-
* 不涉及业务读写。所有业务读写由 *Operations 类组合 db 实例实现。
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import Database from 'better-sqlite3';
|
|
14
|
-
import { readFileSync, existsSync, mkdirSync, statSync } from 'node:fs';
|
|
15
|
-
import { dirname, join } from 'node:path';
|
|
16
|
-
import { fileURLToPath } from 'node:url';
|
|
17
|
-
import { DATABASE } from '../constants.js';
|
|
18
|
-
import { logger } from '../utils/logger.js';
|
|
19
|
-
|
|
20
|
-
export class SQLiteBase {
|
|
21
|
-
protected db: Database.Database;
|
|
22
|
-
protected dbPath: string;
|
|
23
|
-
|
|
24
|
-
constructor(dbPath: string) {
|
|
25
|
-
this.dbPath = dbPath;
|
|
26
|
-
|
|
27
|
-
const dir = dirname(dbPath);
|
|
28
|
-
if (!existsSync(dir)) {
|
|
29
|
-
mkdirSync(dir, { recursive: true });
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
this.db = new Database(dbPath);
|
|
33
|
-
|
|
34
|
-
// WAL mode optimization for better concurrency
|
|
35
|
-
this.db.pragma('journal_mode = WAL');
|
|
36
|
-
this.db.pragma('synchronous = NORMAL');
|
|
37
|
-
this.db.pragma('wal_autocheckpoint = 1000'); // Increased from 100 for better performance
|
|
38
|
-
this.db.pragma(`cache_size = ${DATABASE.CACHE_SIZE}`);
|
|
39
|
-
this.db.pragma(`busy_timeout = ${DATABASE.BUSY_TIMEOUT}`);
|
|
40
|
-
|
|
41
|
-
// Additional performance optimizations
|
|
42
|
-
this.db.pragma('temp_store = MEMORY'); // Store temp tables in memory
|
|
43
|
-
this.db.pragma('mmap_size = 30000000000'); // 30GB memory-mapped I/O
|
|
44
|
-
this.db.pragma('page_size = 4096'); // Optimal page size for most systems
|
|
45
|
-
|
|
46
|
-
// Order matters:
|
|
47
|
-
// 1) runColumnMigrations() ALTERs missing columns on PRE-EXISTING tables.
|
|
48
|
-
// Must run BEFORE initSchema() because schema.sql contains indexes that
|
|
49
|
-
// reference new columns (e.g. idx_skill_invocations_workflow). On a
|
|
50
|
-
// legacy DB those CREATE INDEX statements would otherwise fail because
|
|
51
|
-
// CREATE TABLE IF NOT EXISTS skips the old table and never adds the column.
|
|
52
|
-
// 2) initSchema() applies schema.sql (idempotent CREATE TABLE/INDEX).
|
|
53
|
-
// 3) runIndexMigrations() patches indexes that may still be missing on
|
|
54
|
-
// legacy DBs (defensive; schema.sql already creates them on new DBs).
|
|
55
|
-
this.runColumnMigrations();
|
|
56
|
-
this.initSchema();
|
|
57
|
-
this.runIndexMigrations();
|
|
58
|
-
this.runPostMigrations();
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
private initSchema(): void {
|
|
62
|
-
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
63
|
-
const schemaPath = join(thisDir, 'schema.sql');
|
|
64
|
-
|
|
65
|
-
if (!existsSync(schemaPath)) {
|
|
66
|
-
throw new Error(
|
|
67
|
-
`[SQLiteStorage] schema.sql not found at ${schemaPath}. ` +
|
|
68
|
-
`This indicates a broken build/install. Run \`npm run build\` or reinstall the package.`,
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
const schema = readFileSync(schemaPath, 'utf-8');
|
|
72
|
-
this.db.exec(schema);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
protected hasTable(name: string): boolean {
|
|
76
|
-
try {
|
|
77
|
-
const row = this.db
|
|
78
|
-
.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name = ?`)
|
|
79
|
-
.get(name);
|
|
80
|
-
return !!row;
|
|
81
|
-
} catch {
|
|
82
|
-
return false;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
protected hasColumn(table: string, column: string): boolean {
|
|
87
|
-
try {
|
|
88
|
-
const rows = this.db.prepare(`PRAGMA table_info(${table})`).all() as Array<{ name: string }>;
|
|
89
|
-
return rows.some(r => r.name === column);
|
|
90
|
-
} catch {
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
protected addColumnIfMissing(table: string, column: string, definition: string): void {
|
|
96
|
-
// Skip silently if the table doesn't exist yet — new DBs hit this path on
|
|
97
|
-
// first boot (initSchema runs after column migrations); the column will be
|
|
98
|
-
// created by schema.sql's CREATE TABLE.
|
|
99
|
-
if (!this.hasTable(table)) return;
|
|
100
|
-
if (!this.hasColumn(table, column)) {
|
|
101
|
-
try {
|
|
102
|
-
this.db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
|
|
103
|
-
logger.info(`[SQLiteStorage] migration: added ${table}.${column}`);
|
|
104
|
-
} catch (err) {
|
|
105
|
-
logger.warn(`[SQLiteStorage] migration: ALTER ${table} ADD ${column} failed: ${err}`);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
protected hasIndex(name: string): boolean {
|
|
111
|
-
try {
|
|
112
|
-
const row = this.db
|
|
113
|
-
.prepare(`SELECT name FROM sqlite_master WHERE type='index' AND name = ?`)
|
|
114
|
-
.get(name);
|
|
115
|
-
return !!row;
|
|
116
|
-
} catch {
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Create an index only if it's missing. The CREATE INDEX IF NOT EXISTS already
|
|
123
|
-
* makes this safe; the hasIndex guard makes it explicit which indexes are still
|
|
124
|
-
* being patched by migrations (vs. provided by schema.sql).
|
|
125
|
-
*/
|
|
126
|
-
private createIndexIfMissing(name: string, ddl: string): void {
|
|
127
|
-
if (this.hasIndex(name)) return;
|
|
128
|
-
try {
|
|
129
|
-
this.db.exec(ddl);
|
|
130
|
-
logger.debug(`[SQLiteStorage] migration created ${name}`);
|
|
131
|
-
} catch (err) {
|
|
132
|
-
logger.warn(`[SQLiteStorage] migration: create ${name} failed: ${err}`);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Column migrations run BEFORE initSchema() so legacy tables get missing
|
|
138
|
-
* columns (e.g. skill_invocations.workflow) before schema.sql's CREATE INDEX
|
|
139
|
-
* statements try to reference them.
|
|
140
|
-
*/
|
|
141
|
-
private runColumnMigrations(): void {
|
|
142
|
-
this.addColumnIfMissing('sessions', 'first_prompt', 'TEXT');
|
|
143
|
-
|
|
144
|
-
this.addColumnIfMissing('routing_events', 'skill_confidence', 'REAL');
|
|
145
|
-
this.addColumnIfMissing('routing_events', 'skill_source', 'TEXT');
|
|
146
|
-
|
|
147
|
-
this.addColumnIfMissing('skill_invocations', 'workflow', 'TEXT');
|
|
148
|
-
this.addColumnIfMissing('skill_invocations', 'phase', 'TEXT');
|
|
149
|
-
this.addColumnIfMissing('skill_invocations', 'feature_slug', 'TEXT');
|
|
150
|
-
this.addColumnIfMissing('skill_invocations', 'artifact_path', 'TEXT');
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Index migrations run AFTER initSchema(). Each call is guarded by hasIndex
|
|
155
|
-
* so:
|
|
156
|
-
* - new DBs: schema.sql already created the index → skipped
|
|
157
|
-
* - legacy DBs: schema.sql skipped (existing index) for indexes it tries to
|
|
158
|
-
* create or covered above; remaining migration-only / pre-H1 indexes are
|
|
159
|
-
* patched here.
|
|
160
|
-
* CREATE INDEX IF NOT EXISTS is preserved as a second line of defense.
|
|
161
|
-
*/
|
|
162
|
-
private runIndexMigrations(): void {
|
|
163
|
-
this.createIndexIfMissing(
|
|
164
|
-
'idx_skill_invocations_workflow',
|
|
165
|
-
`CREATE INDEX IF NOT EXISTS idx_skill_invocations_workflow ON skill_invocations(workflow, phase)`,
|
|
166
|
-
);
|
|
167
|
-
this.createIndexIfMissing(
|
|
168
|
-
'idx_skill_invocations_feature',
|
|
169
|
-
`CREATE INDEX IF NOT EXISTS idx_skill_invocations_feature ON skill_invocations(feature_slug)`,
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
this.createIndexIfMissing(
|
|
173
|
-
'idx_sessions_start_time',
|
|
174
|
-
`CREATE INDEX IF NOT EXISTS idx_sessions_start_time ON sessions(start_time DESC)`,
|
|
175
|
-
);
|
|
176
|
-
|
|
177
|
-
// Composite indexes for high-frequency session-scoped queries
|
|
178
|
-
this.createIndexIfMissing(
|
|
179
|
-
'idx_events_session_ts',
|
|
180
|
-
`CREATE INDEX IF NOT EXISTS idx_events_session_ts ON events(session_id, timestamp DESC)`,
|
|
181
|
-
);
|
|
182
|
-
this.createIndexIfMissing(
|
|
183
|
-
'idx_routing_events_session_ts',
|
|
184
|
-
`CREATE INDEX IF NOT EXISTS idx_routing_events_session_ts ON routing_events(session_id, ts DESC)`,
|
|
185
|
-
);
|
|
186
|
-
this.createIndexIfMissing(
|
|
187
|
-
'idx_skill_invocations_session_ts',
|
|
188
|
-
`CREATE INDEX IF NOT EXISTS idx_skill_invocations_session_ts ON skill_invocations(session_id, timestamp DESC)`,
|
|
189
|
-
);
|
|
190
|
-
|
|
191
|
-
// Phase 1 Refactor: Additional performance indexes
|
|
192
|
-
this.createIndexIfMissing(
|
|
193
|
-
'idx_routing_events_obeyed_ts',
|
|
194
|
-
`CREATE INDEX IF NOT EXISTS idx_routing_events_obeyed_ts ON routing_events(obeyed, ts DESC)`,
|
|
195
|
-
);
|
|
196
|
-
this.createIndexIfMissing(
|
|
197
|
-
'idx_events_session_hook',
|
|
198
|
-
`CREATE INDEX IF NOT EXISTS idx_events_session_hook ON events(session_id, hook_type, timestamp DESC)`,
|
|
199
|
-
);
|
|
200
|
-
this.createIndexIfMissing(
|
|
201
|
-
'idx_injections_session_handler',
|
|
202
|
-
`CREATE INDEX IF NOT EXISTS idx_injections_session_handler ON injections(session_id, source_handler)`,
|
|
203
|
-
);
|
|
204
|
-
this.createIndexIfMissing(
|
|
205
|
-
'idx_routing_events_type_ts',
|
|
206
|
-
`CREATE INDEX IF NOT EXISTS idx_routing_events_type_ts ON routing_events(routed_to_type, ts DESC)`,
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* One-shot data migrations + deprecated table cleanup.
|
|
212
|
-
*/
|
|
213
|
-
private runPostMigrations(): void {
|
|
214
|
-
this.backfillSessionsIfNeeded();
|
|
215
|
-
|
|
216
|
-
const deprecatedTables = [
|
|
217
|
-
'quality_issues', 'distill_results', 'v2_decisions',
|
|
218
|
-
'v2_tool_events', 'experiment_assignments', 'routing_rule_states',
|
|
219
|
-
];
|
|
220
|
-
for (const table of deprecatedTables) {
|
|
221
|
-
try { this.db.exec(`DROP TABLE IF EXISTS ${table}`); } catch { /* ignore */ }
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* 将 events 表按 session_id 聚合回填到 sessions 表,仅在 sessions 尚未对齐
|
|
227
|
-
* (没有任何一行带 first_prompt)时触发。
|
|
228
|
-
*/
|
|
229
|
-
private backfillSessionsIfNeeded(): void {
|
|
230
|
-
const eventsExists = this.db
|
|
231
|
-
.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='events'`)
|
|
232
|
-
.get();
|
|
233
|
-
if (!eventsExists) return;
|
|
234
|
-
|
|
235
|
-
const aggRow = this.db
|
|
236
|
-
.prepare(`SELECT COUNT(*) AS total, COUNT(first_prompt) AS with_prompt FROM sessions`)
|
|
237
|
-
.get() as { total: number; with_prompt: number };
|
|
238
|
-
|
|
239
|
-
if (aggRow.with_prompt > 0) return;
|
|
240
|
-
|
|
241
|
-
logger.info('[SQLiteStorage] backfilling sessions aggregate from events...');
|
|
242
|
-
try {
|
|
243
|
-
const backfill = this.db.transaction(() => {
|
|
244
|
-
this.db.exec(`
|
|
245
|
-
INSERT INTO sessions (
|
|
246
|
-
session_id, project_path, status, first_prompt,
|
|
247
|
-
start_time, end_time, last_event_time, event_count
|
|
248
|
-
)
|
|
249
|
-
SELECT
|
|
250
|
-
e.session_id,
|
|
251
|
-
e.project_path,
|
|
252
|
-
'active' AS status,
|
|
253
|
-
(SELECT substr(COALESCE(e2.user_prompt, json_extract(e2.tool_input, '$.user_prompt')), 1, 200)
|
|
254
|
-
FROM events e2
|
|
255
|
-
WHERE e2.session_id = e.session_id
|
|
256
|
-
AND e2.hook_type = 'UserPromptSubmit'
|
|
257
|
-
AND (e2.user_prompt IS NOT NULL OR json_extract(e2.tool_input, '$.user_prompt') IS NOT NULL)
|
|
258
|
-
ORDER BY e2.timestamp ASC LIMIT 1) AS first_prompt,
|
|
259
|
-
MIN(e.timestamp) AS start_time,
|
|
260
|
-
MAX(e.timestamp) AS end_time,
|
|
261
|
-
MAX(e.timestamp) AS last_event_time,
|
|
262
|
-
COUNT(*) AS event_count
|
|
263
|
-
FROM events e
|
|
264
|
-
GROUP BY e.session_id, e.project_path
|
|
265
|
-
ON CONFLICT(session_id) DO UPDATE SET
|
|
266
|
-
first_prompt = COALESCE(sessions.first_prompt, excluded.first_prompt),
|
|
267
|
-
end_time = MAX(COALESCE(sessions.end_time, ''), excluded.end_time),
|
|
268
|
-
last_event_time = MAX(COALESCE(sessions.last_event_time, ''), excluded.last_event_time),
|
|
269
|
-
event_count = excluded.event_count,
|
|
270
|
-
updated_at = datetime('now')
|
|
271
|
-
`);
|
|
272
|
-
});
|
|
273
|
-
backfill();
|
|
274
|
-
const migrated = this.db.prepare('SELECT COUNT(*) AS c FROM sessions').get() as { c: number };
|
|
275
|
-
logger.info(`[SQLiteStorage] backfilled ${migrated.c} sessions from events`);
|
|
276
|
-
} catch (err) {
|
|
277
|
-
logger.warn(`[SQLiteStorage] backfillSessionsIfNeeded failed: ${err}`);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* @internal 仅供 *Operations 子类、migration、初始化使用。
|
|
283
|
-
*
|
|
284
|
-
* Routes / Handlers / Analytics 禁止直接调用。需要查询请走 SQLiteStorage facade
|
|
285
|
-
* 上的具名方法(query* / get* / list* / count* / aggregate*);若 facade 缺方法,
|
|
286
|
-
* 应在对应 Operations 类下沉 SQL 并补 facade 转发,而不是 db.prepare() 直写。
|
|
287
|
-
*
|
|
288
|
-
* 历史:H2 (2026-05) 已收敛 27 处越权 SQL,新增越权请走同样的下沉模式。
|
|
289
|
-
*/
|
|
290
|
-
getDatabase(): Database.Database {
|
|
291
|
-
return this.db;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
getDbPath(): string {
|
|
295
|
-
return this.dbPath;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
protected getDbSizeMb(): number {
|
|
299
|
-
if (!existsSync(this.dbPath)) return 0;
|
|
300
|
-
return statSync(this.dbPath).size / (1024 * 1024);
|
|
301
|
-
}
|
|
302
|
-
}
|