@winspan/claude-forge 8.41.0 → 8.50.6
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/.claude/CLAUDE.md +17 -0
- package/.eslintrc.js +23 -0
- package/.prettierrc +8 -0
- package/ARCHITECTURE_ISSUES.md +249 -0
- package/CLAUDE.md +265 -0
- package/CLAUDE.md.backup +488 -0
- package/DEVELOPMENT.md +310 -0
- package/dist/claudemd/claudemd-generator.d.ts +38 -3
- package/dist/claudemd/claudemd-generator.d.ts.map +1 -1
- package/dist/claudemd/claudemd-generator.js +629 -11
- package/dist/claudemd/claudemd-generator.js.map +1 -1
- package/dist/claudemd/index.d.ts +2 -2
- package/dist/claudemd/index.d.ts.map +1 -1
- package/dist/claudemd/index.js.map +1 -1
- package/dist/claudemd/resume-manager.d.ts.map +1 -1
- package/dist/claudemd/resume-manager.js +5 -2
- package/dist/claudemd/resume-manager.js.map +1 -1
- package/dist/claudemd/tech-detector.d.ts +1 -0
- package/dist/claudemd/tech-detector.d.ts.map +1 -1
- package/dist/claudemd/tech-detector.js +53 -0
- package/dist/claudemd/tech-detector.js.map +1 -1
- package/dist/cli/commands/claudemd.js +2 -2
- package/dist/cli/commands/claudemd.js.map +1 -1
- package/dist/cli/commands/daemon.d.ts +28 -0
- package/dist/cli/commands/daemon.d.ts.map +1 -1
- package/dist/cli/commands/daemon.js +200 -8
- package/dist/cli/commands/daemon.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +3 -35
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/menu.js +10 -10
- package/dist/cli/commands/menu.js.map +1 -1
- package/dist/cli/commands/skills.d.ts.map +1 -1
- package/dist/cli/commands/skills.js +8 -2
- package/dist/cli/commands/skills.js.map +1 -1
- package/dist/cli/commands/stats.d.ts.map +1 -1
- package/dist/cli/commands/stats.js +0 -17
- package/dist/cli/commands/stats.js.map +1 -1
- package/dist/cli/commands/trace.d.ts +9 -0
- package/dist/cli/commands/trace.d.ts.map +1 -0
- package/dist/cli/commands/trace.js +137 -0
- package/dist/cli/commands/trace.js.map +1 -0
- package/dist/cli/index.js +2 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/core/ai/provider.d.ts +10 -2
- package/dist/core/ai/provider.d.ts.map +1 -1
- package/dist/core/ai/provider.js.map +1 -1
- package/dist/core/ai/types.d.ts +1 -19
- package/dist/core/ai/types.d.ts.map +1 -1
- package/dist/core/ai/types.js +1 -1
- package/dist/core/config.d.ts +2 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +23 -6
- package/dist/core/config.js.map +1 -1
- package/dist/core/constants.d.ts +2 -2
- package/dist/core/constants.js +2 -2
- package/dist/core/constants.js.map +1 -1
- package/dist/core/queue/index.d.ts +52 -0
- package/dist/core/queue/index.d.ts.map +1 -0
- package/dist/core/queue/index.js +176 -0
- package/dist/core/queue/index.js.map +1 -0
- package/dist/core/storage/base.d.ts +33 -0
- package/dist/core/storage/base.d.ts.map +1 -0
- package/dist/core/storage/base.js +211 -0
- package/dist/core/storage/base.js.map +1 -0
- package/dist/core/storage/events.d.ts +52 -0
- package/dist/core/storage/events.d.ts.map +1 -0
- package/dist/core/storage/events.js +201 -0
- package/dist/core/storage/events.js.map +1 -0
- package/dist/core/storage/injections.d.ts +27 -0
- package/dist/core/storage/injections.d.ts.map +1 -0
- package/dist/core/storage/injections.js +51 -0
- package/dist/core/storage/injections.js.map +1 -0
- package/dist/core/storage/maintenance.d.ts +21 -0
- package/dist/core/storage/maintenance.d.ts.map +1 -0
- package/dist/core/storage/maintenance.js +52 -0
- package/dist/core/storage/maintenance.js.map +1 -0
- package/dist/core/storage/routing.d.ts +71 -0
- package/dist/core/storage/routing.d.ts.map +1 -0
- package/dist/core/storage/routing.js +141 -0
- package/dist/core/storage/routing.js.map +1 -0
- package/dist/core/storage/rows.d.ts +0 -47
- package/dist/core/storage/rows.d.ts.map +1 -1
- package/dist/core/storage/schema.sql +74 -136
- package/dist/core/storage/sessions.d.ts +34 -0
- package/dist/core/storage/sessions.d.ts.map +1 -0
- package/dist/core/storage/sessions.js +78 -0
- package/dist/core/storage/sessions.js.map +1 -0
- package/dist/core/storage/skills.d.ts +40 -0
- package/dist/core/storage/skills.d.ts.map +1 -0
- package/dist/core/storage/skills.js +107 -0
- package/dist/core/storage/skills.js.map +1 -0
- package/dist/core/storage/sqlite.d.ts +63 -265
- package/dist/core/storage/sqlite.d.ts.map +1 -1
- package/dist/core/storage/sqlite.js +102 -759
- package/dist/core/storage/sqlite.js.map +1 -1
- package/dist/core/storage/tasks.d.ts +64 -0
- package/dist/core/storage/tasks.d.ts.map +1 -0
- package/dist/core/storage/tasks.js +134 -0
- package/dist/core/storage/tasks.js.map +1 -0
- package/dist/core/storage/token-usage.d.ts +36 -0
- package/dist/core/storage/token-usage.d.ts.map +1 -0
- package/dist/core/storage/token-usage.js +59 -0
- package/dist/core/storage/token-usage.js.map +1 -0
- package/dist/core/types.d.ts +60 -4
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +24 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/utils/format.d.ts +28 -0
- package/dist/core/utils/format.d.ts.map +1 -0
- package/dist/core/utils/format.js +68 -0
- package/dist/core/utils/format.js.map +1 -0
- package/dist/core/utils/logger.d.ts +6 -1
- package/dist/core/utils/logger.d.ts.map +1 -1
- package/dist/core/utils/logger.js +72 -2
- package/dist/core/utils/logger.js.map +1 -1
- package/dist/core/utils/session.d.ts +16 -0
- package/dist/core/utils/session.d.ts.map +1 -0
- package/dist/core/utils/session.js +25 -0
- package/dist/core/utils/session.js.map +1 -0
- package/dist/core/utils/time.d.ts +22 -0
- package/dist/core/utils/time.d.ts.map +1 -0
- package/dist/core/utils/time.js +38 -0
- package/dist/core/utils/time.js.map +1 -0
- package/dist/daemon/handlers/history-exporter.d.ts.map +1 -1
- package/dist/daemon/handlers/history-exporter.js +6 -4
- package/dist/daemon/handlers/history-exporter.js.map +1 -1
- package/dist/daemon/handlers/post-tool-use.d.ts +5 -12
- package/dist/daemon/handlers/post-tool-use.d.ts.map +1 -1
- package/dist/daemon/handlers/post-tool-use.js +21 -79
- package/dist/daemon/handlers/post-tool-use.js.map +1 -1
- package/dist/daemon/handlers/stop.d.ts +24 -12
- package/dist/daemon/handlers/stop.d.ts.map +1 -1
- package/dist/daemon/handlers/stop.js +141 -42
- package/dist/daemon/handlers/stop.js.map +1 -1
- package/dist/daemon/handlers/user-prompt.d.ts +18 -19
- package/dist/daemon/handlers/user-prompt.d.ts.map +1 -1
- package/dist/daemon/handlers/user-prompt.js +103 -227
- package/dist/daemon/handlers/user-prompt.js.map +1 -1
- package/dist/daemon/index.d.ts +6 -2
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +76 -120
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/launchd/com.claude-forge.daemon.plist.template +47 -0
- package/dist/daemon/launchd-installer.d.ts +61 -0
- package/dist/daemon/launchd-installer.d.ts.map +1 -0
- package/dist/daemon/launchd-installer.js +182 -0
- package/dist/daemon/launchd-installer.js.map +1 -0
- package/dist/daemon/lifecycle.d.ts +11 -0
- package/dist/daemon/lifecycle.d.ts.map +1 -1
- package/dist/daemon/lifecycle.js +44 -0
- package/dist/daemon/lifecycle.js.map +1 -1
- package/dist/daemon/router.d.ts +9 -2
- package/dist/daemon/router.d.ts.map +1 -1
- package/dist/daemon/router.js +27 -3
- package/dist/daemon/router.js.map +1 -1
- package/dist/daemon/server.d.ts.map +1 -1
- package/dist/daemon/server.js +6 -5
- package/dist/daemon/server.js.map +1 -1
- package/dist/daemon/services/anti-pattern-detector.d.ts +50 -0
- package/dist/daemon/services/anti-pattern-detector.d.ts.map +1 -0
- package/dist/daemon/services/anti-pattern-detector.js +357 -0
- package/dist/daemon/services/anti-pattern-detector.js.map +1 -0
- package/dist/daemon/services/drift-detector.d.ts +64 -0
- package/dist/daemon/services/drift-detector.d.ts.map +1 -0
- package/dist/daemon/services/drift-detector.js +201 -0
- package/dist/daemon/services/drift-detector.js.map +1 -0
- package/dist/{intelligence → daemon/services}/task-segmenter.d.ts +7 -1
- package/dist/daemon/services/task-segmenter.d.ts.map +1 -0
- package/dist/{intelligence → daemon/services}/task-segmenter.js +29 -6
- package/dist/daemon/services/task-segmenter.js.map +1 -0
- package/dist/daemon/services/weekly-report.d.ts +91 -0
- package/dist/daemon/services/weekly-report.d.ts.map +1 -0
- package/dist/daemon/services/weekly-report.js +327 -0
- package/dist/daemon/services/weekly-report.js.map +1 -0
- package/dist/hooks/hook-lib.sh +81 -0
- package/dist/hooks/notification.sh +7 -3
- package/dist/hooks/post-tool-use.sh +8 -4
- package/dist/hooks/pre-tool-use.sh +7 -4
- package/dist/hooks/stop.sh +1 -1
- package/dist/hooks/user-prompt-submit.sh +8 -9
- package/dist/mcp/server.d.ts +2 -2
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +71 -11
- package/dist/mcp/server.js.map +1 -1
- package/dist/skills/invocation-guard.d.ts +20 -0
- package/dist/skills/invocation-guard.d.ts.map +1 -1
- package/dist/skills/invocation-guard.js +63 -0
- package/dist/skills/invocation-guard.js.map +1 -1
- package/dist/skills/matcher.d.ts.map +1 -1
- package/dist/skills/matcher.js +12 -3
- package/dist/skills/matcher.js.map +1 -1
- package/dist/skills/official/code-simplifier.md +16 -0
- package/dist/skills/official/find-skills.md +23 -0
- package/dist/skills/official/official-api-design.md +17 -0
- package/dist/skills/official/official-architecture-decision.md +20 -0
- package/dist/skills/official/official-bmad.md +118 -0
- package/dist/skills/official/official-db-schema-design.md +16 -0
- package/dist/skills/official/official-debug.md +17 -0
- package/dist/skills/official/official-doc-driven.md +31 -0
- package/dist/skills/official/official-harness-engineering.md +108 -0
- package/dist/skills/official/official-performance-optimization.md +30 -0
- package/dist/skills/official/official-pr-review.md +35 -0
- package/dist/skills/official/official-release-checklist.md +30 -0
- package/dist/skills/official/official-security-hardening.md +26 -0
- package/dist/skills/official/official-spec-driven-design.md +31 -0
- package/dist/skills/official/planning-with-files.md +37 -0
- package/dist/skills/official/ui-ux-pro-max.md +18 -0
- package/dist/skills/official/webapp-testing.md +12 -0
- package/dist/skills/official-skills.d.ts +8 -4
- package/dist/skills/official-skills.d.ts.map +1 -1
- package/dist/skills/official-skills.js +48 -704
- package/dist/skills/official-skills.js.map +1 -1
- package/dist/skills/registry.d.ts +5 -0
- package/dist/skills/registry.d.ts.map +1 -1
- package/dist/skills/registry.js +48 -15
- package/dist/skills/registry.js.map +1 -1
- package/dist/skills/tools/pipeline-suggest.d.ts +30 -0
- package/dist/skills/tools/pipeline-suggest.d.ts.map +1 -0
- package/dist/skills/tools/pipeline-suggest.js +178 -0
- package/dist/skills/tools/pipeline-suggest.js.map +1 -0
- package/dist/web/routes/ai.d.ts.map +1 -1
- package/dist/web/routes/ai.js +16 -22
- package/dist/web/routes/ai.js.map +1 -1
- package/dist/web/routes/drift.d.ts +10 -0
- package/dist/web/routes/drift.d.ts.map +1 -0
- package/dist/web/routes/drift.js +21 -0
- package/dist/web/routes/drift.js.map +1 -0
- package/dist/web/routes/error-handler.d.ts +43 -0
- package/dist/web/routes/error-handler.d.ts.map +1 -0
- package/dist/web/routes/error-handler.js +99 -0
- package/dist/web/routes/error-handler.js.map +1 -0
- package/dist/web/routes/insights.d.ts +9 -0
- package/dist/web/routes/insights.d.ts.map +1 -0
- package/dist/web/routes/insights.js +34 -0
- package/dist/web/routes/insights.js.map +1 -0
- package/dist/web/routes/patch.js +2 -2
- package/dist/web/routes/patch.js.map +1 -1
- package/dist/web/routes/reports.d.ts +10 -0
- package/dist/web/routes/reports.d.ts.map +1 -0
- package/dist/web/routes/reports.js +27 -0
- package/dist/web/routes/reports.js.map +1 -0
- package/dist/web/routes/rules.d.ts +10 -3
- package/dist/web/routes/rules.d.ts.map +1 -1
- package/dist/web/routes/rules.js +80 -95
- package/dist/web/routes/rules.js.map +1 -1
- package/dist/web/routes/sessions.d.ts +1 -2
- package/dist/web/routes/sessions.d.ts.map +1 -1
- package/dist/web/routes/sessions.js +27 -39
- package/dist/web/routes/sessions.js.map +1 -1
- package/dist/web/routes/skill-stats.d.ts.map +1 -1
- package/dist/web/routes/skill-stats.js +38 -0
- package/dist/web/routes/skill-stats.js.map +1 -1
- package/dist/web/routes/skills.d.ts.map +1 -1
- package/dist/web/routes/skills.js +34 -0
- package/dist/web/routes/skills.js.map +1 -1
- package/dist/web/routes/stats.d.ts +7 -0
- package/dist/web/routes/stats.d.ts.map +1 -0
- package/dist/web/routes/stats.js +44 -0
- package/dist/web/routes/stats.js.map +1 -0
- package/dist/web/routes/status.js +1 -1
- package/dist/web/routes/status.js.map +1 -1
- package/dist/web/routes/tasks.d.ts +4 -0
- package/dist/web/routes/tasks.d.ts.map +1 -0
- package/dist/web/routes/tasks.js +181 -0
- package/dist/web/routes/tasks.js.map +1 -0
- package/dist/web/routes/trace.d.ts +10 -0
- package/dist/web/routes/trace.d.ts.map +1 -0
- package/dist/web/routes/trace.js +123 -0
- package/dist/web/routes/trace.js.map +1 -0
- package/dist/web/routes/types.d.ts +1 -14
- package/dist/web/routes/types.d.ts.map +1 -1
- package/dist/web/routes/types.js +8 -17
- package/dist/web/routes/types.js.map +1 -1
- package/dist/web/server.d.ts +1 -9
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +28 -28
- package/dist/web/server.js.map +1 -1
- package/dist/web/static/assets/AIConfig-BQCAQE9D.js +2 -0
- package/dist/web/static/assets/AIConfig-BQCAQE9D.js.map +1 -0
- package/dist/web/static/assets/Dashboard-D7Bo6Kan.js +2 -0
- package/dist/web/static/assets/Dashboard-D7Bo6Kan.js.map +1 -0
- package/dist/web/static/assets/{Drawer-DcU3ln98.js → Drawer-BeHRQxUS.js} +2 -2
- package/dist/web/static/assets/{Drawer-DcU3ln98.js.map → Drawer-BeHRQxUS.js.map} +1 -1
- package/dist/web/static/assets/Events-K_tCY2ti.js +2 -0
- package/dist/web/static/assets/Events-K_tCY2ti.js.map +1 -0
- package/dist/web/static/assets/Reports-BJCmBnc_.js +2 -0
- package/dist/web/static/assets/Reports-BJCmBnc_.js.map +1 -0
- package/dist/web/static/assets/SearchInput-BX2KhMkw.js +2 -0
- package/dist/web/static/assets/SearchInput-BX2KhMkw.js.map +1 -0
- package/dist/web/static/assets/SessionDetail-Bkr-kC7V.js +2 -0
- package/dist/web/static/assets/SessionDetail-Bkr-kC7V.js.map +1 -0
- package/dist/web/static/assets/Sessions-Chx9OCLH.js +2 -0
- package/dist/web/static/assets/Sessions-Chx9OCLH.js.map +1 -0
- package/dist/web/static/assets/Skills-O0GT1i7m.js +2 -0
- package/dist/web/static/assets/Skills-O0GT1i7m.js.map +1 -0
- package/dist/web/static/assets/TaskDetail-5SR8zGzv.js +2 -0
- package/dist/web/static/assets/TaskDetail-5SR8zGzv.js.map +1 -0
- package/dist/web/static/assets/Tasks-DCgDqvOZ.js +2 -0
- package/dist/web/static/assets/Tasks-DCgDqvOZ.js.map +1 -0
- package/dist/web/static/assets/export-L_VBD2p1.js +4 -0
- package/dist/web/static/assets/export-L_VBD2p1.js.map +1 -0
- package/dist/web/static/assets/index-D8AKj26b.css +1 -0
- package/dist/web/static/assets/index-DxIbmNmr.js +3 -0
- package/dist/web/static/assets/index-DxIbmNmr.js.map +1 -0
- package/dist/web/static/assets/{lucide-53bR2rki.js → lucide-fJlPI3H7.js} +68 -38
- package/dist/web/static/assets/lucide-fJlPI3H7.js.map +1 -0
- package/dist/web/static/assets/time-Bxuk0M-C.js +2 -0
- package/dist/web/static/assets/time-Bxuk0M-C.js.map +1 -0
- package/dist/web/static/index.html +3 -3
- package/docs/concurrent-agents.md +129 -0
- package/docs/design/architecture-review-20260516.md +232 -0
- package/docs/design/fix-skills-data-and-set-leak-spec-20260516-1300.md +219 -0
- package/docs/design/hook-failure-queue-spec-20260516-1530.md +204 -0
- package/docs/design/refactor-phase1-spec-20260515-1600.md +543 -0
- package/docs/design/refactor-phase2-spec-20260515-1700.md +424 -0
- package/docs/design/tasks-list-filter-pagination-spec-20260518-0930.md +208 -0
- package/docs/implementation/fix-skills-data-and-set-leak-changelog-20260516-1300.md +104 -0
- package/docs/implementation/hook-failure-queue-changelog-20260516-1530.md +196 -0
- package/docs/implementation/hotfix-daemon-event-reject-20260516-1430.md +56 -0
- package/docs/implementation/refactor-phase1-changelog-20260515-1630.md +354 -0
- package/docs/implementation/refactor-phase2-changelog-20260515-1705.md +421 -0
- package/docs/implementation/tasks-list-filter-pagination-changelog-20260518-0930.md +72 -0
- package/docs/reviews/claudemd-template-sync.md +54 -0
- package/docs/reviews/tasks-filter-pagination.md +80 -0
- package/docs/ruflo-learning-strategy.md +322 -0
- package/docs/skills-deduplication-analysis.md +83 -0
- package/docs/skills-multiformat-support.md +177 -0
- package/docs/skills-third-party.md +183 -0
- package/docs/testing/tasks-filter-pagination-test-report.md +86 -0
- package/forge +321 -0
- package/package.json +28 -62
- package/playwright.config.ts +40 -0
- package/scripts/demo-v2.ts +91 -0
- package/scripts/dev-daemon.sh +232 -0
- package/scripts/dev-web.ts +109 -0
- package/scripts/e2e-mcp-link.ts +423 -0
- package/scripts/e2e-methodology-quality.ts +253 -0
- package/scripts/e2e-routing.ts +456 -0
- package/scripts/e2e-user-methodology.ts +326 -0
- package/scripts/e2e-web-workflows.ts +299 -0
- package/scripts/migrate-legacy-to-dynamic.sql +108 -0
- package/scripts/regenerate-execution-docs.ts +116 -0
- package/scripts/sync-agent-skills.ts +193 -0
- package/scripts/test-hook.sh +71 -0
- package/scripts/verify-skill-loading.ts +62 -0
- package/src/claudemd/claudemd-generator.ts +777 -0
- package/src/claudemd/convention-extractor.ts +69 -0
- package/src/claudemd/index.ts +35 -0
- package/src/claudemd/persona-manager.ts +88 -0
- package/src/claudemd/resume-manager.ts +236 -0
- package/src/claudemd/tech-detector.ts +220 -0
- package/src/cli/commands/claudemd.ts +84 -0
- package/src/cli/commands/config.ts +46 -0
- package/src/cli/commands/daemon.ts +310 -0
- package/src/cli/commands/executions.ts +114 -0
- package/src/cli/commands/init.ts +204 -0
- package/src/cli/commands/logs.ts +181 -0
- package/src/cli/commands/mcp.ts +244 -0
- package/src/cli/commands/menu.ts +356 -0
- package/src/cli/commands/skills.ts +185 -0
- package/src/cli/commands/stats.ts +74 -0
- package/src/cli/commands/status.ts +69 -0
- package/src/cli/commands/template.ts +77 -0
- package/src/cli/commands/trace.ts +164 -0
- package/src/cli/index.ts +42 -0
- package/src/cli/init/hook-manager.ts +132 -0
- package/src/core/ai/provider.ts +308 -0
- package/src/core/ai/types.ts +51 -0
- package/src/core/config.ts +124 -0
- package/src/core/constants.ts +45 -0
- package/src/core/queue/index.ts +193 -0
- package/src/core/storage/base.ts +226 -0
- package/src/core/storage/events.ts +255 -0
- package/src/core/storage/injections.ts +78 -0
- package/src/core/storage/maintenance.ts +59 -0
- package/src/core/storage/migrations/002_add_skill_tracking.sql +6 -0
- package/src/core/storage/migrations/003_add_skill_invocations.sql +23 -0
- package/src/core/storage/performance-indexes.sql +23 -0
- package/src/core/storage/routing.ts +194 -0
- package/src/core/storage/rows.ts +112 -0
- package/src/core/storage/schema.sql +214 -0
- package/src/core/storage/sessions.ts +104 -0
- package/src/core/storage/skills.ts +164 -0
- package/src/core/storage/sqlite.ts +194 -0
- package/src/core/storage/tasks.ts +170 -0
- package/src/core/storage/token-usage.ts +93 -0
- package/src/core/types.ts +154 -0
- package/src/core/utils/error-handler.ts +256 -0
- package/src/core/utils/forge-resume-block.ts +74 -0
- package/src/core/utils/format.ts +69 -0
- package/src/core/utils/logger.ts +119 -0
- package/src/core/utils/lru-cache.ts +50 -0
- package/src/core/utils/path.ts +19 -0
- package/src/core/utils/session.ts +26 -0
- package/src/core/utils/time.ts +37 -0
- package/src/core/utils/token-tracker.ts +97 -0
- package/src/daemon/event-parser.ts +35 -0
- package/src/daemon/handlers/history-exporter.ts +117 -0
- package/src/daemon/handlers/post-tool-use.ts +50 -0
- package/src/daemon/handlers/stop.ts +215 -0
- package/src/daemon/handlers/user-prompt.ts +188 -0
- package/src/daemon/index.ts +278 -0
- package/src/daemon/launchd/com.claude-forge.daemon.plist.template +47 -0
- package/src/daemon/launchd-installer.ts +260 -0
- package/src/daemon/lifecycle.ts +128 -0
- package/src/daemon/router.ts +40 -0
- package/src/daemon/server.ts +209 -0
- package/src/daemon/services/anti-pattern-detector.ts +412 -0
- package/src/daemon/services/drift-detector.ts +232 -0
- package/src/daemon/services/task-segmenter.ts +112 -0
- package/src/daemon/services/weekly-report.ts +454 -0
- package/src/hooks/hook-lib.sh +81 -0
- package/src/hooks/notification.sh +35 -0
- package/src/hooks/post-tool-use.sh +61 -0
- package/src/hooks/pre-tool-use.sh +63 -0
- package/src/hooks/stop.sh +40 -0
- package/src/hooks/user-prompt-submit.sh +69 -0
- package/src/mcp/server.ts +322 -0
- package/src/skills/index.ts +2 -0
- package/src/skills/invocation-guard.ts +177 -0
- package/src/skills/matcher.ts +148 -0
- package/src/skills/official/code-simplifier.md +16 -0
- package/src/skills/official/find-skills.md +23 -0
- package/src/skills/official/official-api-design.md +17 -0
- package/src/skills/official/official-architecture-decision.md +20 -0
- package/src/skills/official/official-bmad.md +118 -0
- package/src/skills/official/official-db-schema-design.md +16 -0
- package/src/skills/official/official-debug.md +17 -0
- package/src/skills/official/official-doc-driven.md +31 -0
- package/src/skills/official/official-harness-engineering.md +108 -0
- package/src/skills/official/official-performance-optimization.md +30 -0
- package/src/skills/official/official-pr-review.md +35 -0
- package/src/skills/official/official-release-checklist.md +30 -0
- package/src/skills/official/official-security-hardening.md +26 -0
- package/src/skills/official/official-spec-driven-design.md +31 -0
- package/src/skills/official/planning-with-files.md +37 -0
- package/src/skills/official/ui-ux-pro-max.md +18 -0
- package/src/skills/official/webapp-testing.md +12 -0
- package/src/skills/official-skills.ts +89 -0
- package/src/skills/registry.ts +355 -0
- package/src/skills/semantic-matcher.ts +231 -0
- package/src/skills/tools/pipeline-suggest.ts +226 -0
- package/src/skills/tools/skill-invoke.ts +168 -0
- package/src/skills/tools/skill-list.ts +59 -0
- package/src/templates/go.yaml +53 -0
- package/src/templates/python.yaml +59 -0
- package/src/templates/react.yaml +55 -0
- package/src/templates/template-manager.ts +170 -0
- package/src/web/auth-middleware.ts +55 -0
- package/src/web/routes/ai.ts +204 -0
- package/src/web/routes/auth.ts +22 -0
- package/src/web/routes/drift.ts +25 -0
- package/src/web/routes/error-handler.ts +120 -0
- package/src/web/routes/events.ts +47 -0
- package/src/web/routes/insights.ts +43 -0
- package/src/web/routes/patch.ts +117 -0
- package/src/web/routes/reports.ts +34 -0
- package/src/web/routes/rules.ts +101 -0
- package/src/web/routes/sessions.ts +262 -0
- package/src/web/routes/skill-stats.ts +132 -0
- package/src/web/routes/skills.ts +349 -0
- package/src/web/routes/static.ts +67 -0
- package/src/web/routes/stats.ts +60 -0
- package/src/web/routes/status.ts +30 -0
- package/src/web/routes/tasks.ts +218 -0
- package/src/web/routes/token-usage.ts +20 -0
- package/src/web/routes/trace.ts +138 -0
- package/src/web/routes/types.ts +56 -0
- package/src/web/server.ts +134 -0
- package/src/web/ssrf-guard.ts +112 -0
- package/src/web/static/index.html +3251 -0
- package/src/web/static/vendor/chart.umd.min.js +20 -0
- package/tests/e2e/dashboard.spec.ts +205 -0
- package/tests/e2e/routing-skill-e2e.test.ts +39 -0
- package/tests/helpers/mock-ai.ts +92 -0
- package/tests/helpers/mock-storage.ts +159 -0
- package/tests/integration/queue-replay.integration.test.ts +193 -0
- package/tests/integration/tasks-filter.integration.test.ts +154 -0
- package/tests/performance/database.benchmark.ts +161 -0
- package/tests/semantic-matcher.test.ts +99 -0
- package/tests/skill-matcher.test.ts +110 -0
- package/tests/unit/ai-provider-retry.test.ts +194 -0
- package/tests/unit/ai-provider-vision.test.ts +224 -0
- package/tests/unit/claudemd-generator.test.ts +68 -0
- package/tests/unit/cli-mcp.test.ts +141 -0
- package/tests/unit/handlers.test.ts +171 -0
- package/tests/unit/invocation-guard.test.ts +125 -0
- package/tests/unit/queue.test.ts +272 -0
- package/tests/unit/router.test.ts +138 -0
- package/tests/unit/security.test.ts +128 -0
- package/tests/unit/skill-invocations-workflow.test.ts +495 -0
- package/tests/unit/skill-registry.test.ts +94 -0
- package/tests/unit/skills/invocation-guard-ttl.test.ts +211 -0
- package/tests/unit/skills/official-skills-loader.test.ts +126 -0
- package/tests/unit/skills/registry-multiformat.test.ts +92 -0
- package/tests/unit/storage/sessions-aggregate.test.ts +435 -0
- package/tests/unit/storage/sqlite-refactor-harness.test.ts +314 -0
- package/tests/unit/storage.test.ts +172 -0
- package/tests/unit/token-usage.test.ts +144 -0
- package/tests/unit/type-guards.test.ts +201 -0
- package/tests/unit/utils/format.test.ts +189 -0
- package/tests/unit/utils/session.test.ts +89 -0
- package/tests/unit/utils/time.test.ts +112 -0
- package/tests/unit/web/routes-auth.test.ts +93 -0
- package/tests/unit/web/routes-events.test.ts +101 -0
- package/tests/unit/web/routes-sessions.test.ts +181 -0
- package/tests/unit/web/routes-skill-stats.test.ts +179 -0
- package/tests/unit/web/routes-stats.test.ts +92 -0
- package/tests/unit/web/routes-tasks.test.ts +351 -0
- package/tsconfig.json +22 -0
- package/vitest.config.ts +21 -0
- package/vitest.integration.config.ts +16 -0
- package/web/CLAUDE.md +20 -0
- package/web/index.html +13 -0
- package/web/package-lock.json +4854 -0
- package/web/package.json +35 -0
- package/web/postcss.config.js +6 -0
- package/web/src/App.tsx +110 -0
- package/web/src/components/CodeBlock.tsx +31 -0
- package/web/src/components/Confirm.tsx +96 -0
- package/web/src/components/Drawer.tsx +60 -0
- package/web/src/components/Layout.tsx +145 -0
- package/web/src/components/MarkdownRenderer.tsx +77 -0
- package/web/src/components/SearchInput.tsx +31 -0
- package/web/src/components/SessionDetailContent.tsx +157 -0
- package/web/src/components/Toast.tsx +92 -0
- package/web/src/index.css +19 -0
- package/web/src/main.tsx +31 -0
- package/web/src/pages/AIConfig.tsx +233 -0
- package/web/src/pages/Dashboard.tsx +572 -0
- package/web/src/pages/Events.tsx +271 -0
- package/web/src/pages/Reports.tsx +428 -0
- package/web/src/pages/SessionDetail.tsx +162 -0
- package/web/src/pages/Sessions.tsx +205 -0
- package/web/src/pages/Skills.tsx +180 -0
- package/web/src/pages/TaskDetail.tsx +511 -0
- package/web/src/pages/Tasks.tsx +150 -0
- package/web/src/utils/auth.ts +59 -0
- package/web/src/utils/export.ts +54 -0
- package/web/src/utils/time.ts +13 -0
- package/web/tailwind.config.js +11 -0
- package/web/tsconfig.json +21 -0
- package/web/tsconfig.node.json +10 -0
- package/web/vite.config.ts +76 -0
- package/winspan-claude-forge-8.43.0.tgz +0 -0
- package/dist/agents/definition.d.ts +0 -62
- package/dist/agents/definition.d.ts.map +0 -1
- package/dist/agents/definition.js +0 -27
- package/dist/agents/definition.js.map +0 -1
- package/dist/agents/distributor.d.ts +0 -23
- package/dist/agents/distributor.d.ts.map +0 -1
- package/dist/agents/distributor.js +0 -85
- package/dist/agents/distributor.js.map +0 -1
- package/dist/agents/index.d.ts +0 -5
- package/dist/agents/index.d.ts.map +0 -1
- package/dist/agents/index.js +0 -5
- package/dist/agents/index.js.map +0 -1
- package/dist/agents/methodologies/agent-builder.d.ts +0 -21
- package/dist/agents/methodologies/agent-builder.d.ts.map +0 -1
- package/dist/agents/methodologies/agent-builder.js +0 -149
- package/dist/agents/methodologies/agent-builder.js.map +0 -1
- package/dist/agents/methodologies/phases/bmad/analyze.d.ts +0 -3
- package/dist/agents/methodologies/phases/bmad/analyze.d.ts.map +0 -1
- package/dist/agents/methodologies/phases/bmad/analyze.js +0 -19
- package/dist/agents/methodologies/phases/bmad/analyze.js.map +0 -1
- package/dist/agents/methodologies/phases/bmad/design.d.ts +0 -3
- package/dist/agents/methodologies/phases/bmad/design.d.ts.map +0 -1
- package/dist/agents/methodologies/phases/bmad/design.js +0 -18
- package/dist/agents/methodologies/phases/bmad/design.js.map +0 -1
- package/dist/agents/methodologies/phases/bmad/implement.d.ts +0 -3
- package/dist/agents/methodologies/phases/bmad/implement.d.ts.map +0 -1
- package/dist/agents/methodologies/phases/bmad/implement.js +0 -17
- package/dist/agents/methodologies/phases/bmad/implement.js.map +0 -1
- package/dist/agents/methodologies/phases/bmad/index.d.ts +0 -6
- package/dist/agents/methodologies/phases/bmad/index.d.ts.map +0 -1
- package/dist/agents/methodologies/phases/bmad/index.js +0 -6
- package/dist/agents/methodologies/phases/bmad/index.js.map +0 -1
- package/dist/agents/methodologies/phases/bmad/review.d.ts +0 -3
- package/dist/agents/methodologies/phases/bmad/review.d.ts.map +0 -1
- package/dist/agents/methodologies/phases/bmad/review.js +0 -17
- package/dist/agents/methodologies/phases/bmad/review.js.map +0 -1
- package/dist/agents/methodologies/phases/bmad/test.d.ts +0 -3
- package/dist/agents/methodologies/phases/bmad/test.d.ts.map +0 -1
- package/dist/agents/methodologies/phases/bmad/test.js +0 -21
- package/dist/agents/methodologies/phases/bmad/test.js.map +0 -1
- package/dist/agents/methodologies/phases/harness/fix.d.ts +0 -3
- package/dist/agents/methodologies/phases/harness/fix.d.ts.map +0 -1
- package/dist/agents/methodologies/phases/harness/fix.js +0 -17
- package/dist/agents/methodologies/phases/harness/fix.js.map +0 -1
- package/dist/agents/methodologies/phases/harness/index.d.ts +0 -6
- package/dist/agents/methodologies/phases/harness/index.d.ts.map +0 -1
- package/dist/agents/methodologies/phases/harness/index.js +0 -6
- package/dist/agents/methodologies/phases/harness/index.js.map +0 -1
- package/dist/agents/methodologies/phases/harness/reproduce.d.ts +0 -3
- package/dist/agents/methodologies/phases/harness/reproduce.d.ts.map +0 -1
- package/dist/agents/methodologies/phases/harness/reproduce.js +0 -20
- package/dist/agents/methodologies/phases/harness/reproduce.js.map +0 -1
- package/dist/agents/methodologies/phases/harness/root-cause.d.ts +0 -3
- package/dist/agents/methodologies/phases/harness/root-cause.d.ts.map +0 -1
- package/dist/agents/methodologies/phases/harness/root-cause.js +0 -21
- package/dist/agents/methodologies/phases/harness/root-cause.js.map +0 -1
- package/dist/agents/methodologies/phases/harness/safety-net.d.ts +0 -3
- package/dist/agents/methodologies/phases/harness/safety-net.d.ts.map +0 -1
- package/dist/agents/methodologies/phases/harness/safety-net.js +0 -17
- package/dist/agents/methodologies/phases/harness/safety-net.js.map +0 -1
- package/dist/agents/methodologies/phases/harness/verify.d.ts +0 -3
- package/dist/agents/methodologies/phases/harness/verify.d.ts.map +0 -1
- package/dist/agents/methodologies/phases/harness/verify.js +0 -22
- package/dist/agents/methodologies/phases/harness/verify.js.map +0 -1
- package/dist/agents/methodologies/presets.d.ts +0 -10
- package/dist/agents/methodologies/presets.d.ts.map +0 -1
- package/dist/agents/methodologies/presets.js +0 -79
- package/dist/agents/methodologies/presets.js.map +0 -1
- package/dist/agents/methodologies/types.d.ts +0 -45
- package/dist/agents/methodologies/types.d.ts.map +0 -1
- package/dist/agents/methodologies/types.js +0 -10
- package/dist/agents/methodologies/types.js.map +0 -1
- package/dist/agents/methodologies/user-config-loader.d.ts +0 -30
- package/dist/agents/methodologies/user-config-loader.d.ts.map +0 -1
- package/dist/agents/methodologies/user-config-loader.js +0 -159
- package/dist/agents/methodologies/user-config-loader.js.map +0 -1
- package/dist/agents/official-agents.d.ts +0 -4
- package/dist/agents/official-agents.d.ts.map +0 -1
- package/dist/agents/official-agents.js +0 -559
- package/dist/agents/official-agents.js.map +0 -1
- package/dist/agents/registry.d.ts +0 -57
- package/dist/agents/registry.d.ts.map +0 -1
- package/dist/agents/registry.js +0 -271
- package/dist/agents/registry.js.map +0 -1
- package/dist/capability/index.d.ts +0 -10
- package/dist/capability/index.d.ts.map +0 -1
- package/dist/capability/index.js +0 -10
- package/dist/capability/index.js.map +0 -1
- package/dist/capability/types.d.ts +0 -10
- package/dist/capability/types.d.ts.map +0 -1
- package/dist/capability/types.js +0 -10
- package/dist/capability/types.js.map +0 -1
- package/dist/cli/commands/agents.d.ts +0 -3
- package/dist/cli/commands/agents.d.ts.map +0 -1
- package/dist/cli/commands/agents.js +0 -62
- package/dist/cli/commands/agents.js.map +0 -1
- package/dist/cli/commands/rules.d.ts +0 -8
- package/dist/cli/commands/rules.d.ts.map +0 -1
- package/dist/cli/commands/rules.js +0 -89
- package/dist/cli/commands/rules.js.map +0 -1
- package/dist/daemon/auto-disable-scheduler.d.ts +0 -53
- package/dist/daemon/auto-disable-scheduler.d.ts.map +0 -1
- package/dist/daemon/auto-disable-scheduler.js +0 -114
- package/dist/daemon/auto-disable-scheduler.js.map +0 -1
- package/dist/daemon/handlers/pre-tool-use.d.ts +0 -39
- package/dist/daemon/handlers/pre-tool-use.d.ts.map +0 -1
- package/dist/daemon/handlers/pre-tool-use.js +0 -166
- package/dist/daemon/handlers/pre-tool-use.js.map +0 -1
- package/dist/daemon/routing-observer.d.ts +0 -42
- package/dist/daemon/routing-observer.d.ts.map +0 -1
- package/dist/daemon/routing-observer.js +0 -264
- package/dist/daemon/routing-observer.js.map +0 -1
- package/dist/daemon/routing-state.d.ts +0 -64
- package/dist/daemon/routing-state.d.ts.map +0 -1
- package/dist/daemon/routing-state.js +0 -240
- package/dist/daemon/routing-state.js.map +0 -1
- package/dist/engine/agent-router.d.ts +0 -142
- package/dist/engine/agent-router.d.ts.map +0 -1
- package/dist/engine/agent-router.js +0 -276
- package/dist/engine/agent-router.js.map +0 -1
- package/dist/engine/context-builder.d.ts +0 -23
- package/dist/engine/context-builder.d.ts.map +0 -1
- package/dist/engine/context-builder.js +0 -63
- package/dist/engine/context-builder.js.map +0 -1
- package/dist/engine/conventions/basic-security.yaml +0 -109
- package/dist/engine/conventions/code-quality.yaml +0 -123
- package/dist/engine/conventions/database-safety.yaml +0 -74
- package/dist/engine/conventions/dependency-safety.yaml +0 -132
- package/dist/engine/conventions/docker-safety.yaml +0 -69
- package/dist/engine/conventions/git-safety.yaml +0 -118
- package/dist/engine/conventions/go-best-practices.yaml +0 -84
- package/dist/engine/conventions/python-best-practices.yaml +0 -96
- package/dist/engine/conventions/react-best-practices.yaml +0 -96
- package/dist/engine/conventions/routing.yaml +0 -378
- package/dist/engine/conventions/strict-security.yaml +0 -30
- package/dist/engine/conventions/ts-quality.yaml +0 -49
- package/dist/engine/dsl/compiler.d.ts +0 -34
- package/dist/engine/dsl/compiler.d.ts.map +0 -1
- package/dist/engine/dsl/compiler.js +0 -702
- package/dist/engine/dsl/compiler.js.map +0 -1
- package/dist/engine/dsl/parser.d.ts +0 -25
- package/dist/engine/dsl/parser.d.ts.map +0 -1
- package/dist/engine/dsl/parser.js +0 -208
- package/dist/engine/dsl/parser.js.map +0 -1
- package/dist/engine/dsl/runtime.d.ts +0 -46
- package/dist/engine/dsl/runtime.d.ts.map +0 -1
- package/dist/engine/dsl/runtime.js +0 -173
- package/dist/engine/dsl/runtime.js.map +0 -1
- package/dist/engine/dsl/types.d.ts +0 -139
- package/dist/engine/dsl/types.d.ts.map +0 -1
- package/dist/engine/dsl/types.js +0 -11
- package/dist/engine/dsl/types.js.map +0 -1
- package/dist/engine/evidence-store.d.ts +0 -44
- package/dist/engine/evidence-store.d.ts.map +0 -1
- package/dist/engine/evidence-store.js +0 -109
- package/dist/engine/evidence-store.js.map +0 -1
- package/dist/engine/experiment-router.d.ts +0 -102
- package/dist/engine/experiment-router.d.ts.map +0 -1
- package/dist/engine/experiment-router.js +0 -289
- package/dist/engine/experiment-router.js.map +0 -1
- package/dist/engine/recommender.d.ts +0 -52
- package/dist/engine/recommender.d.ts.map +0 -1
- package/dist/engine/recommender.js +0 -162
- package/dist/engine/recommender.js.map +0 -1
- package/dist/engine/rule-engine.d.ts +0 -33
- package/dist/engine/rule-engine.d.ts.map +0 -1
- package/dist/engine/rule-engine.js +0 -250
- package/dist/engine/rule-engine.js.map +0 -1
- package/dist/engine/security-gates.d.ts +0 -42
- package/dist/engine/security-gates.d.ts.map +0 -1
- package/dist/engine/security-gates.js +0 -210
- package/dist/engine/security-gates.js.map +0 -1
- package/dist/intelligence/classifier.d.ts +0 -75
- package/dist/intelligence/classifier.d.ts.map +0 -1
- package/dist/intelligence/classifier.js +0 -352
- package/dist/intelligence/classifier.js.map +0 -1
- package/dist/intelligence/context-gatherer.d.ts +0 -101
- package/dist/intelligence/context-gatherer.d.ts.map +0 -1
- package/dist/intelligence/context-gatherer.js +0 -417
- package/dist/intelligence/context-gatherer.js.map +0 -1
- package/dist/intelligence/cot-classifier.d.ts +0 -95
- package/dist/intelligence/cot-classifier.d.ts.map +0 -1
- package/dist/intelligence/cot-classifier.js +0 -391
- package/dist/intelligence/cot-classifier.js.map +0 -1
- package/dist/intelligence/distiller.d.ts +0 -22
- package/dist/intelligence/distiller.d.ts.map +0 -1
- package/dist/intelligence/distiller.js +0 -108
- package/dist/intelligence/distiller.js.map +0 -1
- package/dist/intelligence/execution-doc-builder.d.ts +0 -151
- package/dist/intelligence/execution-doc-builder.d.ts.map +0 -1
- package/dist/intelligence/execution-doc-builder.js +0 -1018
- package/dist/intelligence/execution-doc-builder.js.map +0 -1
- package/dist/intelligence/intent-types.d.ts +0 -13
- package/dist/intelligence/intent-types.d.ts.map +0 -1
- package/dist/intelligence/intent-types.js +0 -19
- package/dist/intelligence/intent-types.js.map +0 -1
- package/dist/intelligence/multimodal-parser.d.ts +0 -105
- package/dist/intelligence/multimodal-parser.d.ts.map +0 -1
- package/dist/intelligence/multimodal-parser.js +0 -425
- package/dist/intelligence/multimodal-parser.js.map +0 -1
- package/dist/intelligence/quality-gate.d.ts +0 -45
- package/dist/intelligence/quality-gate.d.ts.map +0 -1
- package/dist/intelligence/quality-gate.js +0 -193
- package/dist/intelligence/quality-gate.js.map +0 -1
- package/dist/intelligence/task-segmenter.d.ts.map +0 -1
- package/dist/intelligence/task-segmenter.js.map +0 -1
- package/dist/web/routes/agents.d.ts +0 -7
- package/dist/web/routes/agents.d.ts.map +0 -1
- package/dist/web/routes/agents.js +0 -209
- package/dist/web/routes/agents.js.map +0 -1
- package/dist/web/routes/execution-trace.d.ts +0 -21
- package/dist/web/routes/execution-trace.d.ts.map +0 -1
- package/dist/web/routes/execution-trace.js +0 -353
- package/dist/web/routes/execution-trace.js.map +0 -1
- package/dist/web/routes/experiments.d.ts +0 -15
- package/dist/web/routes/experiments.d.ts.map +0 -1
- package/dist/web/routes/experiments.js +0 -187
- package/dist/web/routes/experiments.js.map +0 -1
- package/dist/web/routes/routing.d.ts +0 -17
- package/dist/web/routes/routing.d.ts.map +0 -1
- package/dist/web/routes/routing.js +0 -592
- package/dist/web/routes/routing.js.map +0 -1
- package/dist/web/routes/workflows.d.ts +0 -19
- package/dist/web/routes/workflows.d.ts.map +0 -1
- package/dist/web/routes/workflows.js +0 -86
- package/dist/web/routes/workflows.js.map +0 -1
- package/dist/web/static/assets/AIConfig-R5wZ3ZKT.js +0 -2
- package/dist/web/static/assets/AIConfig-R5wZ3ZKT.js.map +0 -1
- package/dist/web/static/assets/Agents-Beg34V1g.js +0 -2
- package/dist/web/static/assets/Agents-Beg34V1g.js.map +0 -1
- package/dist/web/static/assets/CodeBlock--H53gk46.js +0 -2
- package/dist/web/static/assets/CodeBlock--H53gk46.js.map +0 -1
- package/dist/web/static/assets/Dashboard-Cy1xsj1J.js +0 -2
- package/dist/web/static/assets/Dashboard-Cy1xsj1J.js.map +0 -1
- package/dist/web/static/assets/Events-mFhXl4zI.js +0 -2
- package/dist/web/static/assets/Events-mFhXl4zI.js.map +0 -1
- package/dist/web/static/assets/ExecutionTrace-DG901hLR.js +0 -3
- package/dist/web/static/assets/ExecutionTrace-DG901hLR.js.map +0 -1
- package/dist/web/static/assets/MarkdownRenderer-CCIz1MOz.js +0 -2
- package/dist/web/static/assets/MarkdownRenderer-CCIz1MOz.js.map +0 -1
- package/dist/web/static/assets/Routing-B7BFLfjh.js +0 -2
- package/dist/web/static/assets/Routing-B7BFLfjh.js.map +0 -1
- package/dist/web/static/assets/SessionDetail-BT0l4RrK.js +0 -2
- package/dist/web/static/assets/SessionDetail-BT0l4RrK.js.map +0 -1
- package/dist/web/static/assets/Sessions-C6J_HQ_u.js +0 -2
- package/dist/web/static/assets/Sessions-C6J_HQ_u.js.map +0 -1
- package/dist/web/static/assets/Skills-4DQWLaTv.js +0 -2
- package/dist/web/static/assets/Skills-4DQWLaTv.js.map +0 -1
- package/dist/web/static/assets/WorkflowDetail-zhNqUkBE.js +0 -2
- package/dist/web/static/assets/WorkflowDetail-zhNqUkBE.js.map +0 -1
- package/dist/web/static/assets/Workflows-Btvi-lGw.js +0 -2
- package/dist/web/static/assets/Workflows-Btvi-lGw.js.map +0 -1
- package/dist/web/static/assets/export-BQQZLaHV.js +0 -4
- package/dist/web/static/assets/export-BQQZLaHV.js.map +0 -1
- package/dist/web/static/assets/index-Cgr9qMtq.js +0 -3
- package/dist/web/static/assets/index-Cgr9qMtq.js.map +0 -1
- package/dist/web/static/assets/index-CngWb5gC.css +0 -1
- package/dist/web/static/assets/lucide-53bR2rki.js.map +0 -1
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E2E 测试:管理后台 Dashboard
|
|
3
|
+
*
|
|
4
|
+
* 运行前提:
|
|
5
|
+
* 1. 安装 Playwright: npm install -D @playwright/test
|
|
6
|
+
* 2. 启动守护进程: claude-forge daemon start
|
|
7
|
+
* 3. 运行测试: npm run test:e2e
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { test, expect } from '@playwright/test';
|
|
11
|
+
|
|
12
|
+
const BASE_URL = 'http://localhost:3721';
|
|
13
|
+
|
|
14
|
+
test.describe('Dashboard 页面', () => {
|
|
15
|
+
test('应该正确加载并显示统计卡片', async ({ page }) => {
|
|
16
|
+
await page.goto(BASE_URL);
|
|
17
|
+
|
|
18
|
+
// 等待页面加载
|
|
19
|
+
await page.waitForSelector('#dash-stats');
|
|
20
|
+
|
|
21
|
+
// 检查统计卡片是否存在
|
|
22
|
+
await expect(page.locator('.stat-card')).toHaveCount(4);
|
|
23
|
+
|
|
24
|
+
// 检查守护进程状态
|
|
25
|
+
const status = await page.locator('#daemon-status').textContent();
|
|
26
|
+
expect(['运行中', '离线', '检查中']).toContain(status);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('应该显示近 7 天活动图表', async ({ page }) => {
|
|
30
|
+
await page.goto(BASE_URL);
|
|
31
|
+
|
|
32
|
+
// 检查图表容器
|
|
33
|
+
await expect(page.locator('#chart-activity')).toBeVisible();
|
|
34
|
+
await expect(page.locator('#chart-tools')).toBeVisible();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('应该显示最近会话列表', async ({ page }) => {
|
|
38
|
+
await page.goto(BASE_URL);
|
|
39
|
+
|
|
40
|
+
// 检查会话列表容器
|
|
41
|
+
await expect(page.locator('#dash-sessions')).toBeVisible();
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test.describe('Sessions 页面', () => {
|
|
46
|
+
test('应该正确加载会话列表', async ({ page }) => {
|
|
47
|
+
await page.goto(BASE_URL);
|
|
48
|
+
|
|
49
|
+
// 点击 Sessions 导航
|
|
50
|
+
await page.click('#nav-sessions');
|
|
51
|
+
|
|
52
|
+
// 等待页面切换
|
|
53
|
+
await page.waitForSelector('#page-sessions.active');
|
|
54
|
+
|
|
55
|
+
// 检查搜索框
|
|
56
|
+
await expect(page.locator('#sessions-search')).toBeVisible();
|
|
57
|
+
|
|
58
|
+
// 检查列表容器
|
|
59
|
+
await expect(page.locator('#sessions-list')).toBeVisible();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('搜索功能应该正常工作', async ({ page }) => {
|
|
63
|
+
await page.goto(BASE_URL);
|
|
64
|
+
await page.click('#nav-sessions');
|
|
65
|
+
await page.waitForSelector('#page-sessions.active');
|
|
66
|
+
|
|
67
|
+
// 输入搜索关键词
|
|
68
|
+
await page.fill('#sessions-search', 'test');
|
|
69
|
+
|
|
70
|
+
// 等待搜索结果更新(通过 oninput 触发)
|
|
71
|
+
await page.waitForTimeout(300);
|
|
72
|
+
|
|
73
|
+
// 验证搜索框有值
|
|
74
|
+
const searchValue = await page.inputValue('#sessions-search');
|
|
75
|
+
expect(searchValue).toBe('test');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('点击会话应该打开详情抽屉', async ({ page }) => {
|
|
79
|
+
await page.goto(BASE_URL);
|
|
80
|
+
await page.click('#nav-sessions');
|
|
81
|
+
await page.waitForSelector('#page-sessions.active');
|
|
82
|
+
|
|
83
|
+
// 等待会话列表加载
|
|
84
|
+
await page.waitForTimeout(500);
|
|
85
|
+
|
|
86
|
+
// 如果有会话,点击第一个
|
|
87
|
+
const firstSession = page.locator('.list-item').first();
|
|
88
|
+
if (await firstSession.isVisible()) {
|
|
89
|
+
await firstSession.click();
|
|
90
|
+
|
|
91
|
+
// 检查抽屉是否打开
|
|
92
|
+
await expect(page.locator('#drawer.open')).toBeVisible();
|
|
93
|
+
await expect(page.locator('#drawer-title')).toContainText('会话');
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test.describe('Events 页面', () => {
|
|
99
|
+
test('应该正确加载事件列表', async ({ page }) => {
|
|
100
|
+
await page.goto(BASE_URL);
|
|
101
|
+
await page.click('#nav-events');
|
|
102
|
+
await page.waitForSelector('#page-events.active');
|
|
103
|
+
|
|
104
|
+
// 检查搜索框和筛选器
|
|
105
|
+
await expect(page.locator('#events-search')).toBeVisible();
|
|
106
|
+
await expect(page.locator('#events-type')).toBeVisible();
|
|
107
|
+
|
|
108
|
+
// 检查表格
|
|
109
|
+
await expect(page.locator('#events-body')).toBeVisible();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('类型筛选应该正常工作', async ({ page }) => {
|
|
113
|
+
await page.goto(BASE_URL);
|
|
114
|
+
await page.click('#nav-events');
|
|
115
|
+
await page.waitForSelector('#page-events.active');
|
|
116
|
+
|
|
117
|
+
// 选择筛选类型
|
|
118
|
+
await page.selectOption('#events-type', 'PreToolUse');
|
|
119
|
+
|
|
120
|
+
// 等待筛选结果更新
|
|
121
|
+
await page.waitForTimeout(300);
|
|
122
|
+
|
|
123
|
+
// 验证筛选器有值
|
|
124
|
+
const selectedValue = await page.inputValue('#events-type');
|
|
125
|
+
expect(selectedValue).toBe('PreToolUse');
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test.describe('Injections 页面', () => {
|
|
130
|
+
test('应该正确加载注入列表', async ({ page }) => {
|
|
131
|
+
await page.goto(BASE_URL);
|
|
132
|
+
await page.click('#nav-injections');
|
|
133
|
+
await page.waitForSelector('#page-injections.active');
|
|
134
|
+
|
|
135
|
+
// 检查搜索框和筛选器
|
|
136
|
+
await expect(page.locator('#inj-search')).toBeVisible();
|
|
137
|
+
await expect(page.locator('#inj-handler')).toBeVisible();
|
|
138
|
+
|
|
139
|
+
// 检查列表容器
|
|
140
|
+
await expect(page.locator('#inj-list')).toBeVisible();
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test.describe('Rules 页面', () => {
|
|
145
|
+
test('应该正确加载规则列表', async ({ page }) => {
|
|
146
|
+
await page.goto(BASE_URL);
|
|
147
|
+
await page.click('#nav-rules');
|
|
148
|
+
await page.waitForSelector('#page-rules.active');
|
|
149
|
+
|
|
150
|
+
// 检查搜索框
|
|
151
|
+
await expect(page.locator('#rules-search')).toBeVisible();
|
|
152
|
+
|
|
153
|
+
// 检查列表容器
|
|
154
|
+
await expect(page.locator('#rules-list')).toBeVisible();
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test.describe('Live 页面', () => {
|
|
159
|
+
test('应该正确加载实时监控页面', async ({ page }) => {
|
|
160
|
+
await page.goto(BASE_URL);
|
|
161
|
+
await page.click('#nav-live');
|
|
162
|
+
await page.waitForSelector('#page-live.active');
|
|
163
|
+
|
|
164
|
+
// 检查状态和按钮
|
|
165
|
+
await expect(page.locator('#live-status')).toBeVisible();
|
|
166
|
+
await expect(page.locator('#live-btn')).toBeVisible();
|
|
167
|
+
|
|
168
|
+
// 检查日志容器
|
|
169
|
+
await expect(page.locator('#live-log')).toBeVisible();
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test.describe('导航功能', () => {
|
|
174
|
+
test('所有导航链接应该正常工作', async ({ page }) => {
|
|
175
|
+
await page.goto(BASE_URL);
|
|
176
|
+
|
|
177
|
+
const navItems = [
|
|
178
|
+
{ id: 'nav-dashboard', pageId: 'page-dashboard' },
|
|
179
|
+
{ id: 'nav-sessions', pageId: 'page-sessions' },
|
|
180
|
+
{ id: 'nav-events', pageId: 'page-events' },
|
|
181
|
+
{ id: 'nav-injections', pageId: 'page-injections' },
|
|
182
|
+
{ id: 'nav-live', pageId: 'page-live' },
|
|
183
|
+
{ id: 'nav-rules', pageId: 'page-rules' }
|
|
184
|
+
];
|
|
185
|
+
|
|
186
|
+
for (const item of navItems) {
|
|
187
|
+
await page.click(`#${item.id}`);
|
|
188
|
+
await page.waitForSelector(`#${item.pageId}.active`);
|
|
189
|
+
await expect(page.locator(`#${item.pageId}`)).toHaveClass(/active/);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test('刷新按钮应该正常工作', async ({ page }) => {
|
|
194
|
+
await page.goto(BASE_URL);
|
|
195
|
+
|
|
196
|
+
// 点击刷新按钮
|
|
197
|
+
await page.click('button:has-text("刷新")');
|
|
198
|
+
|
|
199
|
+
// 等待页面重新加载
|
|
200
|
+
await page.waitForTimeout(500);
|
|
201
|
+
|
|
202
|
+
// 验证页面仍然可见
|
|
203
|
+
await expect(page.locator('#dash-stats')).toBeVisible();
|
|
204
|
+
});
|
|
205
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E2E Test Suite: Routing & Skill Invocation Verification
|
|
3
|
+
*
|
|
4
|
+
* Tests the complete chain:
|
|
5
|
+
* 1. UserPrompt → Classifier → AgentRouter → routing_events
|
|
6
|
+
* 2. Agent execution → skill_invoke MCP call → skill_invocations
|
|
7
|
+
* 3. Data integrity: obeyed tracking, route_request_id linkage
|
|
8
|
+
*
|
|
9
|
+
* Prerequisites:
|
|
10
|
+
* - Daemon must be running (claude-forge daemon start)
|
|
11
|
+
* - Database at ~/.claude-forge/data.db
|
|
12
|
+
* - MCP server mounted at http://127.0.0.1:3721/mcp
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { describe, it, expect, beforeAll } from 'vitest';
|
|
16
|
+
import { readFileSync } from 'node:fs';
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
import { FORGE_PATHS } from '../../src/core/constants.js';
|
|
19
|
+
import { SQLiteStorage } from '../../src/core/storage/sqlite.js';
|
|
20
|
+
|
|
21
|
+
const DAEMON_TOKEN_PATH = join(FORGE_PATHS.home(), 'daemon.token');
|
|
22
|
+
const DB_PATH = FORGE_PATHS.database();
|
|
23
|
+
|
|
24
|
+
describe('E2E: Routing & Skill Invocation', () => {
|
|
25
|
+
let storage: SQLiteStorage;
|
|
26
|
+
let daemonToken: string;
|
|
27
|
+
|
|
28
|
+
beforeAll(() => {
|
|
29
|
+
// Read daemon token for MCP auth
|
|
30
|
+
try {
|
|
31
|
+
daemonToken = readFileSync(DAEMON_TOKEN_PATH, 'utf-8').trim();
|
|
32
|
+
} catch (err) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`Cannot read daemon token at ${DAEMON_TOKEN_PATH}. ` +
|
|
35
|
+
`Is the daemon running? Run: claude-forge daemon start`
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Connect to live database
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { AIProvider } from '../../src/core/ai/provider.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Mock AI Provider for testing
|
|
5
|
+
*/
|
|
6
|
+
export class MockAIProvider implements AIProvider {
|
|
7
|
+
private responses: string[] = [];
|
|
8
|
+
private currentIndex = 0;
|
|
9
|
+
public callHistory: Array<{ prompt: string; options?: Record<string, unknown> }> = [];
|
|
10
|
+
|
|
11
|
+
constructor(responses: string[] = ['mock response']) {
|
|
12
|
+
this.responses = responses;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async complete(prompt: string, options?: { maxTokens?: number; temperature?: number }): Promise<string> {
|
|
16
|
+
this.callHistory.push({ prompt, options });
|
|
17
|
+
|
|
18
|
+
if (this.currentIndex >= this.responses.length) {
|
|
19
|
+
return this.responses[this.responses.length - 1] || 'default mock response';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const response = this.responses[this.currentIndex];
|
|
23
|
+
this.currentIndex++;
|
|
24
|
+
return response;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Add more responses to the queue
|
|
29
|
+
*/
|
|
30
|
+
addResponse(response: string): void {
|
|
31
|
+
this.responses.push(response);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Reset the provider state
|
|
36
|
+
*/
|
|
37
|
+
reset(): void {
|
|
38
|
+
this.currentIndex = 0;
|
|
39
|
+
this.callHistory = [];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get the number of times complete() was called
|
|
44
|
+
*/
|
|
45
|
+
getCallCount(): number {
|
|
46
|
+
return this.callHistory.length;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get the last prompt sent to complete()
|
|
51
|
+
*/
|
|
52
|
+
getLastPrompt(): string | undefined {
|
|
53
|
+
return this.callHistory[this.callHistory.length - 1]?.prompt;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Create a mock AI provider that always returns the same response
|
|
59
|
+
*/
|
|
60
|
+
export function createMockAI(response: string = 'mock response'): MockAIProvider {
|
|
61
|
+
return new MockAIProvider([response]);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Create a mock AI provider with multiple responses (returns them in sequence)
|
|
66
|
+
*/
|
|
67
|
+
export function createMockAIWithResponses(responses: string[]): MockAIProvider {
|
|
68
|
+
return new MockAIProvider(responses);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Create a mock AI provider that throws an error
|
|
73
|
+
*/
|
|
74
|
+
export function createFailingMockAI(errorMessage: string = 'Mock AI error'): AIProvider {
|
|
75
|
+
return {
|
|
76
|
+
async complete(): Promise<string> {
|
|
77
|
+
throw new Error(errorMessage);
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Create a mock AI provider that simulates slow responses
|
|
84
|
+
*/
|
|
85
|
+
export function createSlowMockAI(response: string = 'slow response', delayMs: number = 100): AIProvider {
|
|
86
|
+
return {
|
|
87
|
+
async complete(): Promise<string> {
|
|
88
|
+
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
89
|
+
return response;
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import type { ForgeEvent } from '../../src/core/types.js';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Create a temporary in-memory SQLite database for testing
|
|
9
|
+
*/
|
|
10
|
+
export function createMockDatabase(): Database.Database {
|
|
11
|
+
const db = new Database(':memory:');
|
|
12
|
+
|
|
13
|
+
// Create minimal schema
|
|
14
|
+
db.exec(`
|
|
15
|
+
CREATE TABLE IF NOT EXISTS events (
|
|
16
|
+
event_id TEXT PRIMARY KEY,
|
|
17
|
+
session_id TEXT NOT NULL,
|
|
18
|
+
project_path TEXT NOT NULL,
|
|
19
|
+
timestamp TEXT NOT NULL,
|
|
20
|
+
hook_type TEXT NOT NULL,
|
|
21
|
+
tool_name TEXT,
|
|
22
|
+
tool_input TEXT,
|
|
23
|
+
tool_output TEXT,
|
|
24
|
+
user_prompt TEXT,
|
|
25
|
+
ai_response TEXT,
|
|
26
|
+
distilled INTEGER DEFAULT 0,
|
|
27
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
31
|
+
session_id TEXT PRIMARY KEY,
|
|
32
|
+
project_path TEXT NOT NULL,
|
|
33
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
34
|
+
first_prompt TEXT,
|
|
35
|
+
start_time TEXT NOT NULL,
|
|
36
|
+
end_time TEXT,
|
|
37
|
+
last_event_time TEXT,
|
|
38
|
+
event_count INTEGER DEFAULT 0,
|
|
39
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
40
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
CREATE TABLE IF NOT EXISTS routing_events (
|
|
44
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
45
|
+
session_id TEXT NOT NULL,
|
|
46
|
+
route_request_id TEXT,
|
|
47
|
+
project_path TEXT NOT NULL,
|
|
48
|
+
ts INTEGER NOT NULL,
|
|
49
|
+
prompt TEXT NOT NULL,
|
|
50
|
+
intent_json TEXT NOT NULL,
|
|
51
|
+
routed_to_type TEXT,
|
|
52
|
+
routed_to_name TEXT,
|
|
53
|
+
is_forced INTEGER DEFAULT 0,
|
|
54
|
+
obeyed INTEGER,
|
|
55
|
+
classification_ms INTEGER
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
CREATE TABLE IF NOT EXISTS injections (
|
|
59
|
+
id TEXT PRIMARY KEY,
|
|
60
|
+
event_id TEXT,
|
|
61
|
+
session_id TEXT NOT NULL,
|
|
62
|
+
timestamp TEXT NOT NULL,
|
|
63
|
+
source_handler TEXT NOT NULL,
|
|
64
|
+
injection_type TEXT NOT NULL,
|
|
65
|
+
content TEXT NOT NULL,
|
|
66
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
CREATE INDEX idx_events_session ON events(session_id);
|
|
70
|
+
CREATE INDEX idx_events_timestamp ON events(timestamp DESC);
|
|
71
|
+
CREATE INDEX idx_sessions_project ON sessions(project_path);
|
|
72
|
+
CREATE INDEX idx_routing_events_session ON routing_events(session_id);
|
|
73
|
+
CREATE INDEX idx_injections_session ON injections(session_id);
|
|
74
|
+
`);
|
|
75
|
+
|
|
76
|
+
return db;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Create a temporary file-based database for testing
|
|
81
|
+
*/
|
|
82
|
+
export function createTempDatabase(): { db: Database.Database; path: string; cleanup: () => void } {
|
|
83
|
+
const dbPath = path.join(tmpdir(), `test-${Date.now()}-${Math.random().toString(36).slice(2)}.db`);
|
|
84
|
+
const db = createMockDatabase();
|
|
85
|
+
|
|
86
|
+
const cleanup = () => {
|
|
87
|
+
try {
|
|
88
|
+
db.close();
|
|
89
|
+
if (fs.existsSync(dbPath)) {
|
|
90
|
+
fs.unlinkSync(dbPath);
|
|
91
|
+
}
|
|
92
|
+
} catch (err) {
|
|
93
|
+
// Ignore cleanup errors
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
return { db, path: dbPath, cleanup };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Create mock ForgeEvent for testing
|
|
102
|
+
*/
|
|
103
|
+
export function createMockEvent(overrides?: Partial<ForgeEvent>): ForgeEvent {
|
|
104
|
+
return {
|
|
105
|
+
event_id: `event-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
106
|
+
session_id: 'test-session',
|
|
107
|
+
project_path: '/test/project',
|
|
108
|
+
timestamp: new Date().toISOString(),
|
|
109
|
+
hook_type: 'UserPromptSubmit',
|
|
110
|
+
user_prompt: 'test prompt',
|
|
111
|
+
...overrides,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Insert mock events into database
|
|
117
|
+
*/
|
|
118
|
+
export function insertMockEvents(db: Database.Database, events: ForgeEvent[]): void {
|
|
119
|
+
const stmt = db.prepare(`
|
|
120
|
+
INSERT INTO events (event_id, session_id, project_path, timestamp, hook_type,
|
|
121
|
+
tool_name, tool_input, tool_output, user_prompt, ai_response, distilled)
|
|
122
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0)
|
|
123
|
+
`);
|
|
124
|
+
|
|
125
|
+
for (const event of events) {
|
|
126
|
+
stmt.run(
|
|
127
|
+
event.event_id || `event-${Date.now()}`,
|
|
128
|
+
event.session_id,
|
|
129
|
+
event.project_path,
|
|
130
|
+
event.timestamp,
|
|
131
|
+
event.hook_type,
|
|
132
|
+
event.tool_name ?? null,
|
|
133
|
+
event.tool_input ? JSON.stringify(event.tool_input) : null,
|
|
134
|
+
event.tool_output ? JSON.stringify(event.tool_output) : null,
|
|
135
|
+
event.user_prompt ?? null,
|
|
136
|
+
event.ai_response ?? null,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Query all events from database
|
|
143
|
+
*/
|
|
144
|
+
export function queryAllEvents(db: Database.Database): ForgeEvent[] {
|
|
145
|
+
const rows = db.prepare('SELECT * FROM events ORDER BY timestamp DESC').all() as Array<Record<string, unknown>>;
|
|
146
|
+
|
|
147
|
+
return rows.map(row => ({
|
|
148
|
+
event_id: row.event_id as string,
|
|
149
|
+
session_id: row.session_id as string,
|
|
150
|
+
project_path: row.project_path as string,
|
|
151
|
+
timestamp: row.timestamp as string,
|
|
152
|
+
hook_type: row.hook_type as ForgeEvent['hook_type'],
|
|
153
|
+
tool_name: (row.tool_name as string) || undefined,
|
|
154
|
+
tool_input: row.tool_input ? JSON.parse(row.tool_input as string) : undefined,
|
|
155
|
+
tool_output: row.tool_output ? JSON.parse(row.tool_output as string) : undefined,
|
|
156
|
+
user_prompt: (row.user_prompt as string) || undefined,
|
|
157
|
+
ai_response: (row.ai_response as string) || undefined,
|
|
158
|
+
}));
|
|
159
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration test: hook failure queue → daemon startup replay
|
|
3
|
+
*
|
|
4
|
+
* Uses real SQLiteStorage (no mocks for storage layer).
|
|
5
|
+
* Does NOT start an actual daemon process — instead tests the queue
|
|
6
|
+
* module functions directly with the same storage that daemon would use.
|
|
7
|
+
*
|
|
8
|
+
* Test scenarios:
|
|
9
|
+
* 1. Enqueue 3 events → replayQueue → all 3 in DB, queue empty
|
|
10
|
+
* 2. Second replayQueue → skipped=3 (dedup), DB still has 3 rows
|
|
11
|
+
* 3. TTL and bad JSON → dead-lettered correctly
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
15
|
+
import { mkdirSync, mkdtempSync, readdirSync, rmSync, writeFileSync, utimesSync } from 'node:fs';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import { tmpdir } from 'node:os';
|
|
18
|
+
import { randomUUID } from 'node:crypto';
|
|
19
|
+
|
|
20
|
+
describe('Queue → Daemon Replay Integration', () => {
|
|
21
|
+
let tempHome: string;
|
|
22
|
+
let queueDir: string;
|
|
23
|
+
let deadDir: string;
|
|
24
|
+
let dbPath: string;
|
|
25
|
+
let originalHome: string | undefined;
|
|
26
|
+
|
|
27
|
+
beforeEach(async () => {
|
|
28
|
+
originalHome = process.env['HOME'];
|
|
29
|
+
tempHome = mkdtempSync(join(tmpdir(), 'forge-integ-'));
|
|
30
|
+
process.env['HOME'] = tempHome;
|
|
31
|
+
|
|
32
|
+
queueDir = join(tempHome, '.claude-forge', 'queue');
|
|
33
|
+
deadDir = join(queueDir, 'dead');
|
|
34
|
+
dbPath = join(tempHome, 'data.db');
|
|
35
|
+
|
|
36
|
+
mkdirSync(queueDir, { recursive: true });
|
|
37
|
+
mkdirSync(deadDir, { recursive: true });
|
|
38
|
+
|
|
39
|
+
vi.resetModules();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
afterEach(() => {
|
|
43
|
+
process.env['HOME'] = originalHome;
|
|
44
|
+
rmSync(tempHome, { recursive: true, force: true });
|
|
45
|
+
vi.resetModules();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('enqueues 3 events then replays all into DB and empties queue', async () => {
|
|
49
|
+
const { enqueueEvent, replayQueue } = await import('../../src/core/queue/index.js');
|
|
50
|
+
const { SQLiteStorage } = await import('../../src/core/storage/sqlite.js');
|
|
51
|
+
const storage = new SQLiteStorage(dbPath);
|
|
52
|
+
|
|
53
|
+
const events = [
|
|
54
|
+
{
|
|
55
|
+
session_id: 'integ-session-1',
|
|
56
|
+
project_path: '/tmp/proj',
|
|
57
|
+
timestamp: new Date().toISOString(),
|
|
58
|
+
hook_type: 'UserPromptSubmit' as const,
|
|
59
|
+
user_prompt: 'prompt 1',
|
|
60
|
+
event_id: randomUUID(),
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
session_id: 'integ-session-1',
|
|
64
|
+
project_path: '/tmp/proj',
|
|
65
|
+
timestamp: new Date().toISOString(),
|
|
66
|
+
hook_type: 'PreToolUse' as const,
|
|
67
|
+
tool_name: 'Bash',
|
|
68
|
+
event_id: randomUUID(),
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
session_id: 'integ-session-1',
|
|
72
|
+
project_path: '/tmp/proj',
|
|
73
|
+
timestamp: new Date().toISOString(),
|
|
74
|
+
hook_type: 'PostToolUse' as const,
|
|
75
|
+
tool_name: 'Bash',
|
|
76
|
+
tool_input: { command: 'ls' },
|
|
77
|
+
tool_output: { output: 'file.txt' },
|
|
78
|
+
event_id: randomUUID(),
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
// Simulate daemon downtime: enqueue all events
|
|
83
|
+
for (const event of events) {
|
|
84
|
+
enqueueEvent(event);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const queuedFiles = readdirSync(queueDir).filter((f) => f.endsWith('.json'));
|
|
88
|
+
expect(queuedFiles).toHaveLength(3);
|
|
89
|
+
|
|
90
|
+
// Simulate daemon startup: replay queue
|
|
91
|
+
const result = await replayQueue(storage);
|
|
92
|
+
expect(result.replayed).toBe(3);
|
|
93
|
+
expect(result.skipped).toBe(0);
|
|
94
|
+
expect(result.dead).toBe(0);
|
|
95
|
+
|
|
96
|
+
// Verify all events are in DB
|
|
97
|
+
const dbEvents = storage.queryEvents({ session_id: 'integ-session-1' });
|
|
98
|
+
expect(dbEvents).toHaveLength(3);
|
|
99
|
+
|
|
100
|
+
// Verify queue is empty
|
|
101
|
+
const remainingFiles = readdirSync(queueDir).filter((f) => f.endsWith('.json'));
|
|
102
|
+
expect(remainingFiles).toHaveLength(0);
|
|
103
|
+
|
|
104
|
+
storage.close();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('second replayQueue skips all events (dedup), DB still has 3 rows', async () => {
|
|
108
|
+
const { enqueueEvent, replayQueue } = await import('../../src/core/queue/index.js');
|
|
109
|
+
const { SQLiteStorage } = await import('../../src/core/storage/sqlite.js');
|
|
110
|
+
const storage = new SQLiteStorage(dbPath);
|
|
111
|
+
|
|
112
|
+
const eventIds = [randomUUID(), randomUUID(), randomUUID()];
|
|
113
|
+
const events = eventIds.map((id, i) => ({
|
|
114
|
+
session_id: 'dedup-session',
|
|
115
|
+
project_path: '/tmp',
|
|
116
|
+
timestamp: new Date().toISOString(),
|
|
117
|
+
hook_type: 'Notification' as const,
|
|
118
|
+
event_id: id,
|
|
119
|
+
user_prompt: `prompt ${i}`,
|
|
120
|
+
}));
|
|
121
|
+
|
|
122
|
+
// First pass: enqueue + replay (all 3 enter DB)
|
|
123
|
+
for (const event of events) {
|
|
124
|
+
enqueueEvent(event);
|
|
125
|
+
}
|
|
126
|
+
const first = await replayQueue(storage);
|
|
127
|
+
expect(first.replayed).toBe(3);
|
|
128
|
+
|
|
129
|
+
// Queue is empty now. Re-enqueue the same events to simulate a second replay
|
|
130
|
+
for (const event of events) {
|
|
131
|
+
enqueueEvent(event);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Second pass: all 3 should be skipped as duplicates
|
|
135
|
+
const second = await replayQueue(storage);
|
|
136
|
+
expect(second.skipped).toBe(3);
|
|
137
|
+
expect(second.replayed).toBe(0);
|
|
138
|
+
|
|
139
|
+
// DB still has exactly 3 events
|
|
140
|
+
const dbEvents = storage.queryEvents({ session_id: 'dedup-session' });
|
|
141
|
+
expect(dbEvents).toHaveLength(3);
|
|
142
|
+
|
|
143
|
+
storage.close();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('dead-letters TTL-expired and corrupt files, replays valid ones', async () => {
|
|
147
|
+
const { enqueueEvent, replayQueue, TTL_DAYS } = await import('../../src/core/queue/index.js');
|
|
148
|
+
const { SQLiteStorage } = await import('../../src/core/storage/sqlite.js');
|
|
149
|
+
const storage = new SQLiteStorage(dbPath);
|
|
150
|
+
|
|
151
|
+
// Valid event
|
|
152
|
+
const validEvent = {
|
|
153
|
+
session_id: 'mixed-session',
|
|
154
|
+
project_path: '/tmp',
|
|
155
|
+
timestamp: new Date().toISOString(),
|
|
156
|
+
hook_type: 'UserPromptSubmit' as const,
|
|
157
|
+
user_prompt: 'valid',
|
|
158
|
+
event_id: randomUUID(),
|
|
159
|
+
};
|
|
160
|
+
enqueueEvent(validEvent);
|
|
161
|
+
|
|
162
|
+
// Corrupt JSON file
|
|
163
|
+
writeFileSync(join(queueDir, '2020-02-01T00-00-00-000Z-corrupt.json'), '{bad json]]');
|
|
164
|
+
|
|
165
|
+
// TTL-expired file (valid JSON but old mtime)
|
|
166
|
+
const oldEventPath = join(queueDir, '2000-01-01T00-00-00-000Z-expired.json');
|
|
167
|
+
writeFileSync(oldEventPath, JSON.stringify({
|
|
168
|
+
session_id: 'mixed-session',
|
|
169
|
+
project_path: '/tmp',
|
|
170
|
+
timestamp: '2000-01-01T00:00:00.000Z',
|
|
171
|
+
hook_type: 'Notification' as const,
|
|
172
|
+
event_id: randomUUID(),
|
|
173
|
+
}));
|
|
174
|
+
const pastTime = new Date(Date.now() - (TTL_DAYS + 2) * 24 * 60 * 60 * 1000);
|
|
175
|
+
utimesSync(oldEventPath, pastTime, pastTime);
|
|
176
|
+
|
|
177
|
+
const result = await replayQueue(storage);
|
|
178
|
+
|
|
179
|
+
expect(result.replayed).toBe(1);
|
|
180
|
+
expect(result.dead).toBe(2); // corrupt + expired
|
|
181
|
+
expect(result.skipped).toBe(0);
|
|
182
|
+
|
|
183
|
+
// dead-letter dir should have 2 files
|
|
184
|
+
const deadFiles = readdirSync(deadDir).filter((f) => f.endsWith('.json'));
|
|
185
|
+
expect(deadFiles).toHaveLength(2);
|
|
186
|
+
|
|
187
|
+
// queue is empty
|
|
188
|
+
const queueFiles = readdirSync(queueDir).filter((f) => f.endsWith('.json'));
|
|
189
|
+
expect(queueFiles).toHaveLength(0);
|
|
190
|
+
|
|
191
|
+
storage.close();
|
|
192
|
+
});
|
|
193
|
+
});
|