@winspan/claude-forge 8.53.2 → 8.54.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DEVELOPMENT.md +290 -221
- package/README.md +50 -8
- package/dist/cli/commands/skills.d.ts.map +1 -1
- package/dist/cli/commands/skills.js +7 -3
- package/dist/cli/commands/skills.js.map +1 -1
- package/dist/cli/init/hook-manager.d.ts +1 -1
- package/dist/cli/init/hook-manager.d.ts.map +1 -1
- package/dist/cli/init/hook-manager.js +1 -0
- package/dist/cli/init/hook-manager.js.map +1 -1
- package/dist/core/storage/events.d.ts.map +1 -1
- package/dist/core/storage/events.js +0 -1
- package/dist/core/storage/events.js.map +1 -1
- package/dist/core/storage/maintenance.d.ts +25 -3
- package/dist/core/storage/maintenance.d.ts.map +1 -1
- package/dist/core/storage/maintenance.js +33 -4
- package/dist/core/storage/maintenance.js.map +1 -1
- package/dist/core/storage/routing.d.ts +4 -0
- package/dist/core/storage/routing.d.ts.map +1 -1
- package/dist/core/storage/routing.js +10 -4
- package/dist/core/storage/routing.js.map +1 -1
- package/dist/core/storage/sessions.d.ts +17 -0
- package/dist/core/storage/sessions.d.ts.map +1 -1
- package/dist/core/storage/sessions.js +64 -0
- package/dist/core/storage/sessions.js.map +1 -1
- package/dist/core/storage/skills.d.ts +4 -0
- package/dist/core/storage/skills.d.ts.map +1 -1
- package/dist/core/storage/skills.js +10 -2
- package/dist/core/storage/skills.js.map +1 -1
- package/dist/core/storage/sqlite.d.ts +5 -0
- package/dist/core/storage/sqlite.d.ts.map +1 -1
- package/dist/core/storage/sqlite.js +6 -0
- package/dist/core/storage/sqlite.js.map +1 -1
- package/dist/core/storage/tasks.d.ts.map +1 -1
- package/dist/core/storage/tasks.js +2 -0
- package/dist/core/storage/tasks.js.map +1 -1
- package/dist/core/types.d.ts +7 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +19 -4
- package/dist/daemon/index.js.map +1 -1
- package/dist/skills/registry.d.ts.map +1 -1
- package/dist/skills/registry.js +13 -2
- package/dist/skills/registry.js.map +1 -1
- package/dist/skills/semantic-matcher.d.ts +2 -2
- package/dist/skills/semantic-matcher.d.ts.map +1 -1
- package/dist/skills/semantic-matcher.js +14 -19
- package/dist/skills/semantic-matcher.js.map +1 -1
- package/dist/skills/upgrade-engine.d.ts +3 -1
- package/dist/skills/upgrade-engine.d.ts.map +1 -1
- package/dist/skills/upgrade-engine.js +25 -14
- package/dist/skills/upgrade-engine.js.map +1 -1
- package/dist/web/analytics/weekly-report.d.ts.map +1 -1
- package/dist/web/analytics/weekly-report.js +21 -29
- package/dist/web/analytics/weekly-report.js.map +1 -1
- package/dist/web/routes/patch.d.ts.map +1 -1
- package/dist/web/routes/patch.js +32 -2
- package/dist/web/routes/patch.js.map +1 -1
- package/dist/web/routes/sessions.d.ts.map +1 -1
- package/dist/web/routes/sessions.js +9 -7
- package/dist/web/routes/sessions.js.map +1 -1
- package/dist/web/routes/trace.d.ts.map +1 -1
- package/dist/web/routes/trace.js +2 -3
- package/dist/web/routes/trace.js.map +1 -1
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +3 -2
- package/dist/web/server.js.map +1 -1
- package/package.json +12 -2
- package/scripts/postinstall.cjs +21 -0
- package/.claude/CLAUDE.md +0 -17
- package/.eslintrc.js +0 -23
- package/.prettierrc +0 -8
- package/ARCHITECTURE_ISSUES.md +0 -249
- package/CLAUDE.md +0 -265
- package/CLAUDE.md.backup +0 -488
- package/docs/concurrent-agents.md +0 -129
- package/docs/design/architecture-review-20260516.md +0 -232
- package/docs/design/fix-skills-data-and-set-leak-spec-20260516-1300.md +0 -219
- package/docs/design/h1-storage-aggregation-spec-20260518-1121.md +0 -299
- package/docs/design/h2-getdatabase-encapsulation-spec-20260518-1450.md +0 -191
- package/docs/design/h3-fallback-removal-spec-20260518-1245.md +0 -76
- package/docs/design/h4-index-dedup-spec-20260518-1230.md +0 -109
- package/docs/design/h6-services-migration-spec-20260518-1355.md +0 -82
- package/docs/design/hook-failure-queue-spec-20260516-1530.md +0 -204
- package/docs/design/l1-swarm-protocol-extract-spec-20260518-1605.md +0 -106
- package/docs/design/m10-forge-paths-spec-20260518-1320.md +0 -121
- package/docs/design/m2-m3-tool-input-spec-20260518-1425.md +0 -131
- package/docs/design/m7-routing-event-association-spec-20260518-1545.md +0 -103
- package/docs/design/project-path-gitroot-spec-20260518-1715.md +0 -134
- package/docs/design/refactor-phase1-spec-20260515-1600.md +0 -543
- package/docs/design/refactor-phase2-spec-20260515-1700.md +0 -424
- package/docs/design/skill-ai-upgrade-spec-20260518-1930.md +0 -297
- package/docs/design/task-active-gc-spec-20260518-1745.md +0 -146
- package/docs/design/tasks-list-filter-pagination-spec-20260518-0930.md +0 -208
- package/docs/implementation/daemon-skill-sync-changelog-20260518-2000.md +0 -22
- package/docs/implementation/fix-skills-data-and-set-leak-changelog-20260516-1300.md +0 -104
- package/docs/implementation/h1-storage-aggregation-changelog-20260518-1121.md +0 -82
- package/docs/implementation/h2-final-changelog-20260518-1530.md +0 -61
- package/docs/implementation/h2-phase1-safety-net-changelog-20260518-1450.md +0 -70
- package/docs/implementation/h2-phase2-operations-changelog-20260518-1450.md +0 -120
- package/docs/implementation/h2-phase3-callsites-changelog-20260518-1450.md +0 -71
- package/docs/implementation/h3-fallback-removal-changelog-20260518-1245.md +0 -71
- package/docs/implementation/h4-index-dedup-changelog-20260518-1230.md +0 -60
- package/docs/implementation/h6-services-migration-changelog-20260518-1355.md +0 -46
- package/docs/implementation/h7-m9-defaults-changelog-20260518-1300.md +0 -46
- package/docs/implementation/hook-failure-queue-changelog-20260516-1530.md +0 -196
- package/docs/implementation/hotfix-daemon-event-reject-20260516-1430.md +0 -56
- package/docs/implementation/l1-swarm-protocol-extract-changelog-20260518-1605.md +0 -45
- package/docs/implementation/l3-l4-daemon-perf-changelog-20260518-1410.md +0 -63
- package/docs/implementation/l6-l8-final-cleanup-changelog-20260518-1640.md +0 -38
- package/docs/implementation/m1-m4-m5-l7-cleanup-changelog-20260518-1310.md +0 -58
- package/docs/implementation/m10-forge-paths-changelog-20260518-1320.md +0 -60
- package/docs/implementation/m2-m3-tool-input-changelog-20260518-1425.md +0 -43
- package/docs/implementation/m6-m8-naming-shutdown-changelog-20260518-1340.md +0 -56
- package/docs/implementation/m7-routing-association-changelog-20260518-1545.md +0 -69
- package/docs/implementation/project-path-gitroot-changelog-20260518-1715.md +0 -63
- package/docs/implementation/refactor-phase1-changelog-20260515-1630.md +0 -354
- package/docs/implementation/refactor-phase2-changelog-20260515-1705.md +0 -421
- package/docs/implementation/skill-ai-upgrade-changelog-20260518-1930.md +0 -49
- package/docs/implementation/task-active-gc-changelog-20260518-1745.md +0 -35
- package/docs/implementation/task-title-summary-changelog-20260518-1130.md +0 -39
- package/docs/implementation/tasks-detail-back-loses-filters-changelog-20260518-1100.md +0 -22
- package/docs/implementation/tasks-list-filter-pagination-changelog-20260518-0930.md +0 -72
- package/docs/implementation/tasks-page-white-screen-hotfix-changelog-20260518-1015.md +0 -56
- package/docs/reviews/claudemd-template-sync.md +0 -54
- package/docs/reviews/task-title-summary.md +0 -92
- package/docs/reviews/tasks-detail-back-loses-filters.md +0 -58
- package/docs/reviews/tasks-filter-pagination.md +0 -80
- package/docs/reviews/tasks-page-white-screen-hotfix.md +0 -126
- package/docs/ruflo-learning-strategy.md +0 -322
- package/docs/skills-deduplication-analysis.md +0 -83
- package/docs/skills-multiformat-support.md +0 -177
- package/docs/skills-third-party.md +0 -183
- package/docs/testing/tasks-filter-pagination-test-report.md +0 -86
- package/forge +0 -321
- package/playwright.config.ts +0 -40
- package/scripts/demo-v2.ts +0 -91
- package/scripts/dev-daemon.sh +0 -232
- package/scripts/dev-web.ts +0 -109
- package/scripts/e2e-mcp-link.ts +0 -423
- package/scripts/e2e-methodology-quality.ts +0 -253
- package/scripts/e2e-routing.ts +0 -456
- package/scripts/e2e-user-methodology.ts +0 -326
- package/scripts/e2e-web-workflows.ts +0 -299
- package/scripts/migrate-legacy-to-dynamic.sql +0 -108
- package/scripts/regenerate-execution-docs.ts +0 -116
- package/scripts/sync-agent-skills.ts +0 -193
- package/scripts/test-hook.sh +0 -71
- package/scripts/verify-skill-loading.ts +0 -62
- package/src/claudemd/claudemd-generator.ts +0 -568
- package/src/claudemd/convention-extractor.ts +0 -69
- package/src/claudemd/index.ts +0 -35
- package/src/claudemd/persona-manager.ts +0 -88
- package/src/claudemd/resume-manager.ts +0 -236
- package/src/claudemd/tech-detector.ts +0 -220
- package/src/claudemd/templates/swarm-protocol.md +0 -222
- package/src/cli/commands/claudemd.ts +0 -84
- package/src/cli/commands/config.ts +0 -46
- package/src/cli/commands/daemon.ts +0 -310
- package/src/cli/commands/executions.ts +0 -115
- package/src/cli/commands/init.ts +0 -204
- package/src/cli/commands/logs.ts +0 -181
- package/src/cli/commands/mcp.ts +0 -242
- package/src/cli/commands/menu.ts +0 -357
- package/src/cli/commands/skills.ts +0 -328
- package/src/cli/commands/stats.ts +0 -73
- package/src/cli/commands/status.ts +0 -69
- package/src/cli/commands/template.ts +0 -77
- package/src/cli/commands/trace.ts +0 -148
- package/src/cli/index.ts +0 -42
- package/src/cli/init/hook-manager.ts +0 -132
- package/src/core/ai/provider.ts +0 -308
- package/src/core/ai/types.ts +0 -51
- package/src/core/config.ts +0 -124
- package/src/core/constants.ts +0 -67
- package/src/core/event-fields.ts +0 -32
- package/src/core/queue/index.ts +0 -192
- package/src/core/storage/base.ts +0 -302
- package/src/core/storage/events.ts +0 -434
- package/src/core/storage/injections.ts +0 -78
- package/src/core/storage/maintenance.ts +0 -59
- package/src/core/storage/migrations/002_add_skill_tracking.sql +0 -6
- package/src/core/storage/migrations/003_add_skill_invocations.sql +0 -23
- package/src/core/storage/performance-indexes.sql +0 -23
- package/src/core/storage/routing.ts +0 -322
- package/src/core/storage/rows.ts +0 -112
- package/src/core/storage/schema.sql +0 -224
- package/src/core/storage/sessions.ts +0 -168
- package/src/core/storage/skills.ts +0 -233
- package/src/core/storage/sqlite.ts +0 -293
- package/src/core/storage/tasks.ts +0 -318
- package/src/core/storage/token-usage.ts +0 -93
- package/src/core/types.ts +0 -181
- package/src/core/utils/error-handler.ts +0 -257
- package/src/core/utils/forge-resume-block.ts +0 -74
- package/src/core/utils/format.ts +0 -69
- package/src/core/utils/git.ts +0 -23
- package/src/core/utils/logger.ts +0 -134
- package/src/core/utils/lru-cache.ts +0 -54
- package/src/core/utils/path.ts +0 -19
- package/src/core/utils/session.ts +0 -26
- package/src/core/utils/time.ts +0 -37
- package/src/core/utils/token-tracker.ts +0 -97
- package/src/daemon/event-parser.ts +0 -36
- package/src/daemon/handlers/history-exporter.ts +0 -117
- package/src/daemon/handlers/post-tool-use.ts +0 -54
- package/src/daemon/handlers/stop.ts +0 -208
- package/src/daemon/handlers/user-prompt.ts +0 -178
- package/src/daemon/hook-sync.ts +0 -91
- package/src/daemon/index.ts +0 -312
- package/src/daemon/launchd/com.claude-forge.daemon.plist.template +0 -47
- package/src/daemon/launchd-installer.ts +0 -260
- package/src/daemon/lifecycle.ts +0 -128
- package/src/daemon/router.ts +0 -40
- package/src/daemon/server.ts +0 -196
- package/src/daemon/services/task-segmenter.ts +0 -112
- package/src/daemon/skill-sync.ts +0 -88
- package/src/hooks/hook-lib.sh +0 -118
- package/src/hooks/notification.sh +0 -35
- package/src/hooks/post-tool-use.sh +0 -61
- package/src/hooks/pre-tool-use.sh +0 -63
- package/src/hooks/stop.sh +0 -43
- package/src/hooks/user-prompt-submit.sh +0 -69
- package/src/mcp/server.ts +0 -322
- package/src/skills/index.ts +0 -2
- package/src/skills/invocation-guard.ts +0 -177
- package/src/skills/matcher.ts +0 -148
- package/src/skills/official/code-simplifier.md +0 -52
- package/src/skills/official/find-skills.md +0 -142
- package/src/skills/official/official-api-design.md +0 -30
- package/src/skills/official/official-architecture-decision.md +0 -41
- package/src/skills/official/official-bmad.md +0 -118
- package/src/skills/official/official-db-schema-design.md +0 -34
- package/src/skills/official/official-debug.md +0 -25
- package/src/skills/official/official-doc-driven.md +0 -31
- package/src/skills/official/official-harness-engineering.md +0 -108
- package/src/skills/official/official-performance-optimization.md +0 -30
- package/src/skills/official/official-pr-review.md +0 -35
- package/src/skills/official/official-release-checklist.md +0 -30
- package/src/skills/official/official-security-hardening.md +0 -32
- package/src/skills/official/official-spec-driven-design.md +0 -31
- package/src/skills/official/planning-with-files.md +0 -241
- package/src/skills/official/ui-ux-pro-max.md +0 -105
- package/src/skills/official/webapp-testing.md +0 -96
- package/src/skills/official-skills.ts +0 -89
- package/src/skills/registry.ts +0 -355
- package/src/skills/semantic-matcher.ts +0 -234
- package/src/skills/tools/pipeline-suggest.ts +0 -226
- package/src/skills/tools/skill-invoke.ts +0 -168
- package/src/skills/tools/skill-list.ts +0 -59
- package/src/skills/upgrade-engine.ts +0 -541
- package/src/skills/upgrade-prompt.ts +0 -84
- package/src/templates/go.yaml +0 -53
- package/src/templates/python.yaml +0 -59
- package/src/templates/react.yaml +0 -55
- package/src/templates/template-manager.ts +0 -170
- package/src/web/analytics/anti-pattern-detector.ts +0 -367
- package/src/web/analytics/drift-detector.ts +0 -219
- package/src/web/analytics/weekly-report.ts +0 -431
- package/src/web/auth-middleware.ts +0 -54
- package/src/web/routes/_helpers.ts +0 -34
- package/src/web/routes/ai.ts +0 -204
- package/src/web/routes/auth.ts +0 -22
- package/src/web/routes/drift.ts +0 -25
- package/src/web/routes/error-handler.ts +0 -120
- package/src/web/routes/events.ts +0 -47
- package/src/web/routes/insights.ts +0 -43
- package/src/web/routes/patch.ts +0 -117
- package/src/web/routes/reports.ts +0 -34
- package/src/web/routes/rules.ts +0 -76
- package/src/web/routes/sessions.ts +0 -250
- package/src/web/routes/skill-stats.ts +0 -92
- package/src/web/routes/skills.ts +0 -350
- package/src/web/routes/static.ts +0 -67
- package/src/web/routes/stats.ts +0 -50
- package/src/web/routes/status.ts +0 -30
- package/src/web/routes/tasks.ts +0 -193
- package/src/web/routes/token-usage.ts +0 -20
- package/src/web/routes/trace.ts +0 -126
- package/src/web/routes/types.ts +0 -57
- package/src/web/server.ts +0 -134
- package/src/web/ssrf-guard.ts +0 -112
- package/src/web/static/index.html +0 -3251
- package/src/web/static/vendor/chart.umd.min.js +0 -20
- package/tests/e2e/dashboard.spec.ts +0 -205
- package/tests/e2e/routing-skill-e2e.test.ts +0 -39
- package/tests/helpers/mock-ai.ts +0 -92
- package/tests/helpers/mock-storage.ts +0 -159
- package/tests/integration/claudemd-generator.test.ts +0 -90
- package/tests/integration/queue-replay.integration.test.ts +0 -193
- package/tests/integration/tasks-filter.integration.test.ts +0 -154
- package/tests/integration/web-analytics.integration.test.ts +0 -133
- package/tests/integration/web-stats.integration.test.ts +0 -135
- package/tests/integration/web-trace.integration.test.ts +0 -175
- package/tests/performance/database.benchmark.ts +0 -161
- package/tests/semantic-matcher.test.ts +0 -99
- package/tests/skill-matcher.test.ts +0 -110
- package/tests/unit/ai-provider-retry.test.ts +0 -194
- package/tests/unit/ai-provider-vision.test.ts +0 -224
- package/tests/unit/claudemd-generator.test.ts +0 -68
- package/tests/unit/cli-mcp.test.ts +0 -141
- package/tests/unit/core/forge-paths.test.ts +0 -99
- package/tests/unit/daemon/hook-sync.test.ts +0 -71
- package/tests/unit/daemon/post-tool-use.test.ts +0 -121
- package/tests/unit/daemon/skill-sync.test.ts +0 -75
- package/tests/unit/daemon/stop-handler-behavior-summary.test.ts +0 -202
- package/tests/unit/daemon/task-segmenter-recover.test.ts +0 -84
- package/tests/unit/event-fields.test.ts +0 -88
- package/tests/unit/event-parser.test.ts +0 -55
- package/tests/unit/handlers.test.ts +0 -171
- package/tests/unit/hooks/resolve-project-path.test.ts +0 -122
- package/tests/unit/invocation-guard.test.ts +0 -125
- package/tests/unit/queue.test.ts +0 -272
- package/tests/unit/router.test.ts +0 -138
- package/tests/unit/security.test.ts +0 -128
- package/tests/unit/skill-invocations-workflow.test.ts +0 -495
- package/tests/unit/skill-registry.test.ts +0 -94
- package/tests/unit/skills/invocation-guard-ttl.test.ts +0 -211
- package/tests/unit/skills/official-skills-loader.test.ts +0 -126
- package/tests/unit/skills/registry-multiformat.test.ts +0 -92
- package/tests/unit/skills/upgrade-engine-parse.test.ts +0 -138
- package/tests/unit/skills/upgrade-engine.test.ts +0 -401
- package/tests/unit/skills/upgrade-prompt.test.ts +0 -89
- package/tests/unit/socket-server.test.ts +0 -183
- package/tests/unit/storage/event-operations-aggregates.test.ts +0 -342
- package/tests/unit/storage/migration-idempotent.test.ts +0 -304
- package/tests/unit/storage/routing-aggregates.test.ts +0 -276
- package/tests/unit/storage/routing.test.ts +0 -117
- package/tests/unit/storage/schema-missing.test.ts +0 -81
- package/tests/unit/storage/session-operations-aggregates.test.ts +0 -120
- package/tests/unit/storage/sessions-aggregate.test.ts +0 -435
- package/tests/unit/storage/skill-operations-counts.test.ts +0 -106
- package/tests/unit/storage/skills-aggregates.test.ts +0 -104
- package/tests/unit/storage/sqlite-refactor-harness.test.ts +0 -314
- package/tests/unit/storage/task-operations-counts.test.ts +0 -46
- package/tests/unit/storage/tasks-getById.test.ts +0 -343
- package/tests/unit/storage/tasks-stale-gc.test.ts +0 -86
- package/tests/unit/storage.test.ts +0 -172
- package/tests/unit/token-usage.test.ts +0 -144
- package/tests/unit/type-guards.test.ts +0 -201
- package/tests/unit/utils/format.test.ts +0 -189
- package/tests/unit/utils/session.test.ts +0 -89
- package/tests/unit/utils/time.test.ts +0 -112
- package/tests/unit/web/navigation-back-contract.test.ts +0 -134
- package/tests/unit/web/routes-auth.test.ts +0 -93
- package/tests/unit/web/routes-events.test.ts +0 -101
- package/tests/unit/web/routes-rules.test.ts +0 -182
- package/tests/unit/web/routes-sessions.test.ts +0 -181
- package/tests/unit/web/routes-skill-stats.test.ts +0 -179
- package/tests/unit/web/routes-stats.test.ts +0 -92
- package/tests/unit/web/routes-tasks.test.ts +0 -385
- package/tests/unit/web/task-title-contract.test.ts +0 -210
- package/tests/unit/web/tasks-component-contract.test.ts +0 -179
- package/tsconfig.json +0 -22
- package/vitest.config.ts +0 -21
- package/vitest.integration.config.ts +0 -16
- package/web/CLAUDE.md +0 -20
- package/web/index.html +0 -13
- package/web/package-lock.json +0 -4854
- package/web/package.json +0 -35
- package/web/postcss.config.js +0 -6
- package/web/src/App.tsx +0 -110
- package/web/src/components/CodeBlock.tsx +0 -31
- package/web/src/components/Confirm.tsx +0 -96
- package/web/src/components/Drawer.tsx +0 -60
- package/web/src/components/Layout.tsx +0 -145
- package/web/src/components/MarkdownRenderer.tsx +0 -77
- package/web/src/components/SearchInput.tsx +0 -31
- package/web/src/components/SessionDetailContent.tsx +0 -157
- package/web/src/components/Toast.tsx +0 -92
- package/web/src/index.css +0 -19
- package/web/src/main.tsx +0 -31
- package/web/src/pages/AIConfig.tsx +0 -233
- package/web/src/pages/Dashboard.tsx +0 -572
- package/web/src/pages/Events.tsx +0 -271
- package/web/src/pages/Reports.tsx +0 -428
- package/web/src/pages/SessionDetail.tsx +0 -162
- package/web/src/pages/Sessions.tsx +0 -205
- package/web/src/pages/Skills.tsx +0 -180
- package/web/src/pages/TaskDetail.tsx +0 -515
- package/web/src/pages/Tasks.tsx +0 -415
- package/web/src/utils/auth.ts +0 -59
- package/web/src/utils/export.ts +0 -54
- package/web/src/utils/navigation.ts +0 -25
- package/web/src/utils/task-title.ts +0 -49
- package/web/src/utils/time.ts +0 -13
- package/web/tailwind.config.js +0 -11
- package/web/tsconfig.json +0 -21
- package/web/tsconfig.node.json +0 -10
- package/web/vite.config.ts +0 -76
- package/winspan-claude-forge-8.43.0.tgz +0 -0
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import type { SQLiteStorage } from '../../core/storage/sqlite.js';
|
|
3
|
-
import { logger } from '../../core/utils/logger.js';
|
|
4
|
-
import { normalizeTimestamp, timestampToMs } from '../../core/utils/time.js';
|
|
5
|
-
import { truncateSessionId } from '../../core/utils/session.js';
|
|
6
|
-
import { truncateString } from '../../core/utils/format.js';
|
|
7
|
-
|
|
8
|
-
const NEW_TASK_KEYWORDS = [
|
|
9
|
-
'帮我', '实现', '修复', '添加', '重构', '优化', '创建', '删除',
|
|
10
|
-
'升级', '迁移', '部署', '配置', '设计', '提交', '推送',
|
|
11
|
-
'fix', 'add', 'implement', 'refactor', 'create', 'delete', 'deploy',
|
|
12
|
-
];
|
|
13
|
-
|
|
14
|
-
const CONTINUE_KEYWORDS = [
|
|
15
|
-
'继续', '然后', '还有', '另外', '这个', '那个', '它',
|
|
16
|
-
'对', '好的', '可以', '是的', '不是', '不对', '改一下',
|
|
17
|
-
];
|
|
18
|
-
|
|
19
|
-
const TASK_SWITCH_MINUTES = 10;
|
|
20
|
-
|
|
21
|
-
export class TaskSegmenter {
|
|
22
|
-
private currentTasks = new Map<string, { id: string; lastTime: number }>();
|
|
23
|
-
|
|
24
|
-
constructor(private storage: SQLiteStorage) {}
|
|
25
|
-
|
|
26
|
-
processPrompt(sessionId: string, prompt: string, timestamp: string, eventId?: string): string {
|
|
27
|
-
const key = sessionId;
|
|
28
|
-
const current = this.currentTasks.get(key) ?? this.recoverActiveTask(key);
|
|
29
|
-
const now = timestampToMs(timestamp);
|
|
30
|
-
|
|
31
|
-
if (!current || this.shouldStartNewTask(prompt, now, current)) {
|
|
32
|
-
if (current) {
|
|
33
|
-
this.storage.updateTask(current.id, { status: 'completed', end_time: timestamp });
|
|
34
|
-
}
|
|
35
|
-
const taskId = this.createTask(sessionId, prompt, timestamp);
|
|
36
|
-
this.currentTasks.set(key, { id: taskId, lastTime: now });
|
|
37
|
-
if (eventId) this.storage.linkEventToTask(taskId, eventId);
|
|
38
|
-
return taskId;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
current.lastTime = now;
|
|
42
|
-
if (eventId) this.storage.linkEventToTask(current.id, eventId);
|
|
43
|
-
return current.id;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
linkEvent(sessionId: string, eventId: string): void {
|
|
47
|
-
const current = this.currentTasks.get(sessionId) ?? this.recoverActiveTask(sessionId);
|
|
48
|
-
if (current) {
|
|
49
|
-
this.storage.linkEventToTask(current.id, eventId);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* 从数据库恢复 session 的 active 任务到内存 Map。
|
|
55
|
-
* 用于 daemon 重启后内存丢失场景的 lazy 恢复。
|
|
56
|
-
* 找不到 active 任务时返回 null(不报错)。
|
|
57
|
-
*/
|
|
58
|
-
private recoverActiveTask(sessionId: string): { id: string; lastTime: number } | null {
|
|
59
|
-
const tasks = this.storage.queryTasks({ session_id: sessionId, limit: 5 });
|
|
60
|
-
const activeTask = tasks.find(t => t.status === 'active');
|
|
61
|
-
if (!activeTask) return null;
|
|
62
|
-
|
|
63
|
-
const anchor = activeTask.end_time ?? activeTask.start_time;
|
|
64
|
-
const lastTime = timestampToMs(anchor);
|
|
65
|
-
const entry = { id: activeTask.id, lastTime };
|
|
66
|
-
this.currentTasks.set(sessionId, entry);
|
|
67
|
-
logger.info(
|
|
68
|
-
`[TaskSegmenter] Recovered active task ${truncateString(activeTask.id, 8)} for session ${truncateSessionId(sessionId)} from DB`,
|
|
69
|
-
);
|
|
70
|
-
return entry;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
getCurrentTaskId(sessionId: string): string | null {
|
|
74
|
-
return this.currentTasks.get(sessionId)?.id ?? null;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
completeCurrentTask(sessionId: string, timestamp: string): void {
|
|
78
|
-
const current = this.currentTasks.get(sessionId) ?? this.recoverActiveTask(sessionId);
|
|
79
|
-
if (!current) return;
|
|
80
|
-
this.storage.updateTask(current.id, {
|
|
81
|
-
status: 'completed',
|
|
82
|
-
end_time: timestamp,
|
|
83
|
-
});
|
|
84
|
-
this.currentTasks.delete(sessionId);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
private shouldStartNewTask(prompt: string, now: number, current: { lastTime: number }): boolean {
|
|
88
|
-
const minutesDiff = (now - current.lastTime) / 60000;
|
|
89
|
-
if (minutesDiff > TASK_SWITCH_MINUTES) return true;
|
|
90
|
-
|
|
91
|
-
const hasContinue = CONTINUE_KEYWORDS.some(kw => prompt.includes(kw));
|
|
92
|
-
if (hasContinue) return false;
|
|
93
|
-
|
|
94
|
-
const hasNewTask = NEW_TASK_KEYWORDS.some(kw => prompt.includes(kw));
|
|
95
|
-
return hasNewTask;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
private createTask(sessionId: string, prompt: string, timestamp: string): string {
|
|
99
|
-
const taskId = randomUUID();
|
|
100
|
-
const title = this.extractTitle(prompt);
|
|
101
|
-
this.storage.writeTask({ id: taskId, session_id: sessionId, title, start_time: timestamp });
|
|
102
|
-
logger.info(`[TaskSegmenter] New task: "${title}" (${truncateString(taskId, 8)})`);
|
|
103
|
-
return taskId;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
private extractTitle(prompt: string): string {
|
|
107
|
-
const cleaned = prompt.replace(/\[Image #\d+\]/g, '').trim();
|
|
108
|
-
const firstLine = cleaned.split('\n')[0].trim();
|
|
109
|
-
if (firstLine.length <= 60) return firstLine || '(无标题)';
|
|
110
|
-
return truncateString(firstLine, 60);
|
|
111
|
-
}
|
|
112
|
-
}
|
package/src/daemon/skill-sync.ts
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, copyFileSync, mkdirSync, readdirSync } from 'node:fs';
|
|
2
|
-
import { createHash } from 'node:crypto';
|
|
3
|
-
import { join, dirname } from 'node:path';
|
|
4
|
-
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import { homedir } from 'node:os';
|
|
6
|
-
import { logger } from '../core/utils/logger.js';
|
|
7
|
-
|
|
8
|
-
const USER_SKILLS_DIR = join(homedir(), '.claude', 'skills');
|
|
9
|
-
|
|
10
|
-
function sha256(content: Buffer): string {
|
|
11
|
-
return createHash('sha256').update(content).digest('hex');
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function getSourceSkillsDir(): string {
|
|
15
|
-
// Compiled: dist/daemon/skill-sync.js → dist/skills/official
|
|
16
|
-
// Dev (vitest): src/daemon/skill-sync.ts → src/skills/official
|
|
17
|
-
return join(dirname(fileURLToPath(import.meta.url)), '..', 'skills', 'official');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface SkillSyncResult {
|
|
21
|
-
copied: number;
|
|
22
|
-
checked: number;
|
|
23
|
-
skipped_userOwned: number; // user skills not in source list — untouched
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Sync official skills from dist/skills/official to ~/.claude/skills/.
|
|
28
|
-
*
|
|
29
|
-
* Strategy:
|
|
30
|
-
* - Only files present in the source dir (dist/skills/official) are candidates
|
|
31
|
-
* - sha256 identical → skip; different → overwrite
|
|
32
|
-
* - User-only skills (not in source list) are never touched
|
|
33
|
-
* - Target dir is created if absent (first install)
|
|
34
|
-
*
|
|
35
|
-
* Failures are logged as warnings and never thrown — daemon startup must not be blocked.
|
|
36
|
-
*/
|
|
37
|
-
export function syncSkills(opts?: { sourceDir?: string; targetDir?: string }): SkillSyncResult {
|
|
38
|
-
const result: SkillSyncResult = { copied: 0, checked: 0, skipped_userOwned: 0 };
|
|
39
|
-
const sourceDir = opts?.sourceDir ?? getSourceSkillsDir();
|
|
40
|
-
const targetDir = opts?.targetDir ?? USER_SKILLS_DIR;
|
|
41
|
-
|
|
42
|
-
if (!existsSync(sourceDir)) {
|
|
43
|
-
logger.warn(`[SkillSync] source dir not found at ${sourceDir}, skipping`);
|
|
44
|
-
return result;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Ensure target dir exists (first install may not have ~/.claude/skills/)
|
|
48
|
-
try {
|
|
49
|
-
mkdirSync(targetDir, { recursive: true });
|
|
50
|
-
} catch {
|
|
51
|
-
/* ignore — if it already exists mkdirSync is a no-op */
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
let sourceFiles: string[];
|
|
55
|
-
try {
|
|
56
|
-
sourceFiles = readdirSync(sourceDir).filter((f) => f.endsWith('.md'));
|
|
57
|
-
} catch (err) {
|
|
58
|
-
logger.warn(`[SkillSync] failed to list ${sourceDir}: ${err}`);
|
|
59
|
-
return result;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
for (const file of sourceFiles) {
|
|
63
|
-
result.checked++;
|
|
64
|
-
const src = join(sourceDir, file);
|
|
65
|
-
const dest = join(targetDir, file);
|
|
66
|
-
|
|
67
|
-
try {
|
|
68
|
-
const srcContent = readFileSync(src);
|
|
69
|
-
if (existsSync(dest)) {
|
|
70
|
-
const destContent = readFileSync(dest);
|
|
71
|
-
if (sha256(srcContent) === sha256(destContent)) {
|
|
72
|
-
continue; // identical — no copy needed
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
copyFileSync(src, dest);
|
|
77
|
-
result.copied++;
|
|
78
|
-
logger.info(`[SkillSync] updated ${file}`);
|
|
79
|
-
} catch (err) {
|
|
80
|
-
logger.warn(`[SkillSync] failed to sync ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (result.copied > 0) {
|
|
85
|
-
logger.info(`[SkillSync] synced ${result.copied}/${result.checked} skill files`);
|
|
86
|
-
}
|
|
87
|
-
return result;
|
|
88
|
-
}
|
package/src/hooks/hook-lib.sh
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Claude Forge - Hook shared library
|
|
3
|
-
# Source this file at the top of each hook script:
|
|
4
|
-
# source "$(dirname "$0")/hook-lib.sh"
|
|
5
|
-
#
|
|
6
|
-
# Provides:
|
|
7
|
-
# - SOCKET_PATH, QUEUE_DIR shared variables
|
|
8
|
-
# - send_event_or_enqueue() function
|
|
9
|
-
|
|
10
|
-
SOCKET_PATH="${CLAUDE_FORGE_SOCKET:-$HOME/.claude-forge/daemon.sock}"
|
|
11
|
-
QUEUE_DIR="$HOME/.claude-forge/queue"
|
|
12
|
-
|
|
13
|
-
# resolve_project_path <input_cwd>
|
|
14
|
-
#
|
|
15
|
-
# Walk up the directory tree from <input_cwd> to find the nearest ancestor that
|
|
16
|
-
# contains a `.git` entry (directory for normal clones, file for worktrees /
|
|
17
|
-
# submodules). Print the resolved git-root path on stdout.
|
|
18
|
-
#
|
|
19
|
-
# Fallback rules:
|
|
20
|
-
# - Empty input → start from $PWD
|
|
21
|
-
# - Relative input → prefixed with $PWD to absolutise
|
|
22
|
-
# - No .git found within 64 levels → print original input unchanged
|
|
23
|
-
#
|
|
24
|
-
# POSIX-only: uses `dirname` and `case`; does NOT depend on `realpath` or any
|
|
25
|
-
# GNU coreutils extensions, so it works on macOS BSD and Linux alike.
|
|
26
|
-
resolve_project_path() {
|
|
27
|
-
local input="${1:-}"
|
|
28
|
-
local dir="${input:-$PWD}"
|
|
29
|
-
|
|
30
|
-
# Absolutise: prefix relative paths with $PWD (no realpath dependency)
|
|
31
|
-
case "$dir" in
|
|
32
|
-
/*) ;;
|
|
33
|
-
*) dir="$PWD/$dir" ;;
|
|
34
|
-
esac
|
|
35
|
-
|
|
36
|
-
local guard=0
|
|
37
|
-
while [ "$dir" != "/" ] && [ "$dir" != "." ] && [ $guard -lt 64 ]; do
|
|
38
|
-
if [ -d "$dir/.git" ] || [ -f "$dir/.git" ]; then
|
|
39
|
-
printf '%s' "$dir"
|
|
40
|
-
return 0
|
|
41
|
-
fi
|
|
42
|
-
dir=$(dirname "$dir")
|
|
43
|
-
guard=$((guard + 1))
|
|
44
|
-
done
|
|
45
|
-
|
|
46
|
-
# No git ancestor found → fall back to the caller's original cwd
|
|
47
|
-
printf '%s' "${input:-$PWD}"
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
# send_event_or_enqueue <event_json> [timeout_seconds]
|
|
51
|
-
#
|
|
52
|
-
# 1. If socket file does not exist → enqueue in background → return empty string
|
|
53
|
-
# 2. If socket exists → send via nc with timeout
|
|
54
|
-
# - nc exit code != 0 → enqueue in background → return empty string
|
|
55
|
-
# - nc exit code == 0 → return daemon response on stdout
|
|
56
|
-
#
|
|
57
|
-
# The caller reads REPLY_RESPONSE after calling this function:
|
|
58
|
-
# send_event_or_enqueue "$EVENT" 10
|
|
59
|
-
# RESPONSE="$HOOK_RESPONSE"
|
|
60
|
-
#
|
|
61
|
-
# macOS BSD nc notes:
|
|
62
|
-
# - `nc -U` opens a Unix domain socket connection
|
|
63
|
-
# - `-w N` sets idle timeout (seconds); on macOS this is the "connection timeout"
|
|
64
|
-
# for UDP but for TCP/Unix it's inactivity timeout after connection
|
|
65
|
-
# - Exit code: 0 = success (data sent + connection closed by remote), 1 = error
|
|
66
|
-
# - An empty response (daemon returns nothing) still exits 0 if the connection
|
|
67
|
-
# was established and closed cleanly → we check exit code, NOT response content
|
|
68
|
-
|
|
69
|
-
HOOK_RESPONSE=""
|
|
70
|
-
|
|
71
|
-
send_event_or_enqueue() {
|
|
72
|
-
local event_json="$1"
|
|
73
|
-
local timeout_secs="${2:-10}"
|
|
74
|
-
|
|
75
|
-
HOOK_RESPONSE=""
|
|
76
|
-
|
|
77
|
-
# Fast path: no socket file → daemon is not running
|
|
78
|
-
if [ ! -S "$SOCKET_PATH" ]; then
|
|
79
|
-
_enqueue_event_bg "$event_json"
|
|
80
|
-
return 0
|
|
81
|
-
fi
|
|
82
|
-
|
|
83
|
-
# Socket exists: try to send; capture exit code separately from stdout
|
|
84
|
-
local tmp_response
|
|
85
|
-
tmp_response=$(echo "$event_json" | nc -U -w "$timeout_secs" "$SOCKET_PATH" 2>/dev/null)
|
|
86
|
-
local nc_exit=$?
|
|
87
|
-
|
|
88
|
-
if [ $nc_exit -ne 0 ]; then
|
|
89
|
-
# nc failed (connection refused, socket disappeared between check and send, etc.)
|
|
90
|
-
_enqueue_event_bg "$event_json"
|
|
91
|
-
return 0
|
|
92
|
-
fi
|
|
93
|
-
|
|
94
|
-
# Success: pass response back to caller
|
|
95
|
-
HOOK_RESPONSE="$tmp_response"
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
# _enqueue_event_bg <event_json>
|
|
99
|
-
# Forks a background subshell to write the event JSON to the queue directory.
|
|
100
|
-
# The main hook process is never blocked by this operation.
|
|
101
|
-
_enqueue_event_bg() {
|
|
102
|
-
local event_json="$1"
|
|
103
|
-
(
|
|
104
|
-
mkdir -p "$QUEUE_DIR"
|
|
105
|
-
# Timestamp with second precision (BSD date has no %N nanoseconds)
|
|
106
|
-
local ts
|
|
107
|
-
ts=$(date -u +"%Y%m%dT%H%M%S")
|
|
108
|
-
# UUID: prefer uuidgen (macOS), fall back to /proc on Linux
|
|
109
|
-
local uuid
|
|
110
|
-
uuid=$(uuidgen 2>/dev/null \
|
|
111
|
-
|| cat /proc/sys/kernel/random/uuid 2>/dev/null \
|
|
112
|
-
|| printf '%s-%s' "$$" "$RANDOM")
|
|
113
|
-
local filename="${ts}-${uuid}.json"
|
|
114
|
-
# Atomic write: write to temp file, then rename
|
|
115
|
-
local tmp_file="$QUEUE_DIR/.tmp-${filename}"
|
|
116
|
-
printf '%s' "$event_json" > "$tmp_file" && mv "$tmp_file" "$QUEUE_DIR/${filename}"
|
|
117
|
-
) &
|
|
118
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Claude Forge - Notification Hook
|
|
3
|
-
|
|
4
|
-
# shellcheck source=./hook-lib.sh
|
|
5
|
-
source "$(dirname "$0")/hook-lib.sh"
|
|
6
|
-
|
|
7
|
-
AUTH_TOKEN=$(cat "$HOME/.claude-forge/daemon.token" 2>/dev/null || echo '')
|
|
8
|
-
|
|
9
|
-
# 读取 stdin
|
|
10
|
-
INPUT=$(cat)
|
|
11
|
-
|
|
12
|
-
# 提取 cwd(jq 替代 python3)
|
|
13
|
-
RAW_CWD=$(echo "$INPUT" | jq -r '.cwd // ""')
|
|
14
|
-
PROJECT_PATH=$(resolve_project_path "${RAW_CWD:-$PWD}")
|
|
15
|
-
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // .sessionId // ""')
|
|
16
|
-
SESSION_ID="${SESSION_ID:-${CLAUDE_CODE_SESSION_ID:-cli}}"
|
|
17
|
-
|
|
18
|
-
# 构造事件 JSON(原始 INPUT 作为 tool_input,保留完整通知内容)
|
|
19
|
-
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
|
|
20
|
-
TOOL_INPUT=$(echo "$INPUT" | jq -c '.')
|
|
21
|
-
EVENT=$(jq -nc \
|
|
22
|
-
--arg hook_type "Notification" \
|
|
23
|
-
--arg timestamp "$TIMESTAMP" \
|
|
24
|
-
--arg session_id "$SESSION_ID" \
|
|
25
|
-
--arg project_path "$PROJECT_PATH" \
|
|
26
|
-
--argjson tool_input "$TOOL_INPUT" \
|
|
27
|
-
--arg auth "$AUTH_TOKEN" \
|
|
28
|
-
'{hook_type: $hook_type, timestamp: $timestamp, session_id: $session_id,
|
|
29
|
-
project_path: $project_path, tool_name: "notification",
|
|
30
|
-
tool_input: $tool_input, _auth: $auth}')
|
|
31
|
-
|
|
32
|
-
# 发送到 daemon(1 秒超时;失败时入队)
|
|
33
|
-
send_event_or_enqueue "$EVENT" 1
|
|
34
|
-
|
|
35
|
-
exit 0
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Claude Forge - PostToolUse Hook
|
|
3
|
-
|
|
4
|
-
# shellcheck source=./hook-lib.sh
|
|
5
|
-
source "$(dirname "$0")/hook-lib.sh"
|
|
6
|
-
|
|
7
|
-
AUTH_TOKEN=$(cat "$HOME/.claude-forge/daemon.token" 2>/dev/null || echo '')
|
|
8
|
-
|
|
9
|
-
# 读取 stdin
|
|
10
|
-
INPUT=$(cat)
|
|
11
|
-
|
|
12
|
-
# 提取字段(单次 jq 调用,替代 4 个 python3 进程)
|
|
13
|
-
# tool_response 是 Claude Code 的字段名
|
|
14
|
-
TOOL_NAME="${CLAUDE_TOOL_NAME:-$(echo "$INPUT" | jq -r '.tool_name // ""')}"
|
|
15
|
-
TOOL_INPUT=$(echo "$INPUT" | jq -c '.tool_input // {}')
|
|
16
|
-
TOOL_OUTPUT=$(echo "$INPUT" | jq -c '.tool_response // {}')
|
|
17
|
-
RAW_CWD=$(echo "$INPUT" | jq -r '.cwd // ""')
|
|
18
|
-
PROJECT_PATH=$(resolve_project_path "${RAW_CWD:-$PWD}")
|
|
19
|
-
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // .sessionId // ""')
|
|
20
|
-
SESSION_ID="${SESSION_ID:-${CLAUDE_CODE_SESSION_ID:-cli}}"
|
|
21
|
-
|
|
22
|
-
# 构造事件 JSON
|
|
23
|
-
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
|
|
24
|
-
EVENT=$(jq -nc \
|
|
25
|
-
--arg hook_type "PostToolUse" \
|
|
26
|
-
--arg timestamp "$TIMESTAMP" \
|
|
27
|
-
--arg session_id "$SESSION_ID" \
|
|
28
|
-
--arg project_path "$PROJECT_PATH" \
|
|
29
|
-
--arg tool_name "$TOOL_NAME" \
|
|
30
|
-
--argjson tool_input "$TOOL_INPUT" \
|
|
31
|
-
--argjson tool_output "$TOOL_OUTPUT" \
|
|
32
|
-
--arg auth "$AUTH_TOKEN" \
|
|
33
|
-
'{hook_type: $hook_type, timestamp: $timestamp, session_id: $session_id,
|
|
34
|
-
project_path: $project_path, tool_name: $tool_name, tool_input: $tool_input,
|
|
35
|
-
tool_output: $tool_output, _auth: $auth}')
|
|
36
|
-
|
|
37
|
-
# 发送到 daemon(PostToolUse 也需要等待响应以支持通知注入;失败时入队)
|
|
38
|
-
send_event_or_enqueue "$EVENT" 5
|
|
39
|
-
RESPONSE="$HOOK_RESPONSE"
|
|
40
|
-
|
|
41
|
-
if [ -n "$RESPONSE" ]; then
|
|
42
|
-
# 硬阻断:优先使用 hookSpecificOutput(Claude Code 官方格式)
|
|
43
|
-
ALLOW=$(echo "$RESPONSE" | jq -r '.allow // true')
|
|
44
|
-
if [ "$ALLOW" = "false" ]; then
|
|
45
|
-
HAS_HOOK_OUTPUT=$(echo "$RESPONSE" | jq -r 'if .hookSpecificOutput then "yes" else "no" end')
|
|
46
|
-
if [ "$HAS_HOOK_OUTPUT" = "yes" ]; then
|
|
47
|
-
echo "$RESPONSE" | jq '{hookSpecificOutput: .hookSpecificOutput}'
|
|
48
|
-
exit 0
|
|
49
|
-
else
|
|
50
|
-
REASON=$(echo "$RESPONSE" | jq -r '.reason // "Pipeline 已阻断"')
|
|
51
|
-
echo "$REASON" >&2
|
|
52
|
-
exit 2
|
|
53
|
-
fi
|
|
54
|
-
fi
|
|
55
|
-
|
|
56
|
-
HAS_CONTEXT=$(echo "$RESPONSE" | jq -r 'if .additionalContext and (.additionalContext != "") then "yes" else "no" end')
|
|
57
|
-
if [ "$HAS_CONTEXT" = "yes" ]; then
|
|
58
|
-
echo "$RESPONSE"
|
|
59
|
-
fi
|
|
60
|
-
fi
|
|
61
|
-
exit 0
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Claude Forge - PreToolUse Hook(双向通信)
|
|
3
|
-
|
|
4
|
-
# shellcheck source=./hook-lib.sh
|
|
5
|
-
source "$(dirname "$0")/hook-lib.sh"
|
|
6
|
-
|
|
7
|
-
AUTH_TOKEN=$(cat "$HOME/.claude-forge/daemon.token" 2>/dev/null || echo '')
|
|
8
|
-
|
|
9
|
-
# 读取 stdin(Claude Code 传入的 JSON)
|
|
10
|
-
INPUT=$(cat)
|
|
11
|
-
|
|
12
|
-
# 提取字段(单次 jq 调用,替代 3 个 python3 进程)
|
|
13
|
-
TOOL_NAME="${CLAUDE_TOOL_NAME:-$(echo "$INPUT" | jq -r '.tool_name // ""')}"
|
|
14
|
-
TOOL_INPUT=$(echo "$INPUT" | jq -c '.tool_input // {}')
|
|
15
|
-
RAW_CWD=$(echo "$INPUT" | jq -r '.cwd // ""')
|
|
16
|
-
PROJECT_PATH=$(resolve_project_path "${RAW_CWD:-$PWD}")
|
|
17
|
-
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // .sessionId // ""')
|
|
18
|
-
SESSION_ID="${SESSION_ID:-${CLAUDE_CODE_SESSION_ID:-cli}}"
|
|
19
|
-
|
|
20
|
-
# 构造事件 JSON(jq -n 替代 python3 heredoc)
|
|
21
|
-
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
|
|
22
|
-
EVENT=$(jq -nc \
|
|
23
|
-
--arg hook_type "PreToolUse" \
|
|
24
|
-
--arg timestamp "$TIMESTAMP" \
|
|
25
|
-
--arg session_id "$SESSION_ID" \
|
|
26
|
-
--arg project_path "$PROJECT_PATH" \
|
|
27
|
-
--arg tool_name "$TOOL_NAME" \
|
|
28
|
-
--argjson tool_input "$TOOL_INPUT" \
|
|
29
|
-
--arg auth "$AUTH_TOKEN" \
|
|
30
|
-
'{hook_type: $hook_type, timestamp: $timestamp, session_id: $session_id,
|
|
31
|
-
project_path: $project_path, tool_name: $tool_name, tool_input: $tool_input,
|
|
32
|
-
_auth: $auth}')
|
|
33
|
-
|
|
34
|
-
# 发送事件(5 秒超时;失败时入队)
|
|
35
|
-
send_event_or_enqueue "$EVENT" 5
|
|
36
|
-
RESPONSE="$HOOK_RESPONSE"
|
|
37
|
-
|
|
38
|
-
if [ -n "$RESPONSE" ]; then
|
|
39
|
-
ALLOW=$(echo "$RESPONSE" | jq -r '.allow // true')
|
|
40
|
-
|
|
41
|
-
if [ "$ALLOW" = "false" ]; then
|
|
42
|
-
# 优先使用 hookSpecificOutput(Claude Code 官方格式),否则降级到 stderr + exit 2
|
|
43
|
-
HAS_HOOK_OUTPUT=$(echo "$RESPONSE" | jq -r 'if .hookSpecificOutput then "yes" else "no" end')
|
|
44
|
-
if [ "$HAS_HOOK_OUTPUT" = "yes" ]; then
|
|
45
|
-
# 输出 hookSpecificOutput 格式到 stdout,但必须 exit 非0 才能真正阻止工具执行
|
|
46
|
-
echo "$RESPONSE" | jq '{hookSpecificOutput: .hookSpecificOutput}'
|
|
47
|
-
exit 1
|
|
48
|
-
else
|
|
49
|
-
# 降级:输出原因到 stderr,exit 2 强制阻断
|
|
50
|
-
REASON=$(echo "$RESPONSE" | jq -r '.reason // "操作被 Forge 阻断"')
|
|
51
|
-
echo "$REASON" >&2
|
|
52
|
-
exit 2
|
|
53
|
-
fi
|
|
54
|
-
fi
|
|
55
|
-
|
|
56
|
-
# 有 additionalContext 时输出
|
|
57
|
-
HAS_CONTEXT=$(echo "$RESPONSE" | jq -r 'if .additionalContext and (.additionalContext != "") then "yes" else "no" end')
|
|
58
|
-
if [ "$HAS_CONTEXT" = "yes" ]; then
|
|
59
|
-
echo "$RESPONSE"
|
|
60
|
-
fi
|
|
61
|
-
fi
|
|
62
|
-
|
|
63
|
-
exit 0
|
package/src/hooks/stop.sh
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Claude Forge - Stop Hook
|
|
3
|
-
|
|
4
|
-
# shellcheck source=./hook-lib.sh
|
|
5
|
-
source "$(dirname "$0")/hook-lib.sh"
|
|
6
|
-
|
|
7
|
-
AUTH_TOKEN=$(cat "$HOME/.claude-forge/daemon.token" 2>/dev/null || echo '')
|
|
8
|
-
|
|
9
|
-
# 读取 stdin
|
|
10
|
-
INPUT=$(cat 2>/dev/null || echo '{}')
|
|
11
|
-
|
|
12
|
-
# 提取 cwd,上溯 git root
|
|
13
|
-
RAW_CWD=$(echo "$INPUT" | jq -r '.cwd // ""')
|
|
14
|
-
PROJECT_PATH=$(resolve_project_path "${RAW_CWD:-$PWD}")
|
|
15
|
-
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // .sessionId // ""')
|
|
16
|
-
SESSION_ID="${SESSION_ID:-${CLAUDE_CODE_SESSION_ID:-cli}}"
|
|
17
|
-
|
|
18
|
-
# 构造事件 JSON
|
|
19
|
-
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
|
|
20
|
-
EVENT=$(jq -nc \
|
|
21
|
-
--arg hook_type "Stop" \
|
|
22
|
-
--arg timestamp "$TIMESTAMP" \
|
|
23
|
-
--arg session_id "$SESSION_ID" \
|
|
24
|
-
--arg project_path "$PROJECT_PATH" \
|
|
25
|
-
--arg auth "$AUTH_TOKEN" \
|
|
26
|
-
'{hook_type: $hook_type, timestamp: $timestamp, session_id: $session_id,
|
|
27
|
-
project_path: $project_path, tool_name: "stop", tool_input: {}, _auth: $auth}')
|
|
28
|
-
|
|
29
|
-
# 发送事件并读取响应(失败时入队)
|
|
30
|
-
send_event_or_enqueue "$EVENT" 5
|
|
31
|
-
RESPONSE="$HOOK_RESPONSE"
|
|
32
|
-
|
|
33
|
-
# 解析响应:allow=false 时输出 reason 并以 exit 2 阻断
|
|
34
|
-
if [ -n "$RESPONSE" ]; then
|
|
35
|
-
ALLOW=$(echo "$RESPONSE" | jq -r '.allow // true' 2>/dev/null)
|
|
36
|
-
if [ "$ALLOW" = "false" ]; then
|
|
37
|
-
REASON=$(echo "$RESPONSE" | jq -r '.reason // "质量门禁阻断"' 2>/dev/null)
|
|
38
|
-
echo "$REASON"
|
|
39
|
-
exit 2
|
|
40
|
-
fi
|
|
41
|
-
fi
|
|
42
|
-
|
|
43
|
-
exit 0
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Claude Forge - UserPromptSubmit Hook
|
|
3
|
-
# 拦截用户输入,触发意图分析和自动 Pipeline 编排
|
|
4
|
-
|
|
5
|
-
# shellcheck source=./hook-lib.sh
|
|
6
|
-
source "$(dirname "$0")/hook-lib.sh"
|
|
7
|
-
|
|
8
|
-
AUTH_TOKEN=$(cat "$HOME/.claude-forge/daemon.token" 2>/dev/null || echo '')
|
|
9
|
-
|
|
10
|
-
# 读取 stdin(Claude Code 传入的 JSON)
|
|
11
|
-
INPUT=$(cat)
|
|
12
|
-
|
|
13
|
-
# 提取字段(jq 替代 python3)
|
|
14
|
-
USER_PROMPT=$(echo "$INPUT" | jq -r '.prompt // ""')
|
|
15
|
-
RAW_CWD=$(echo "$INPUT" | jq -r '.cwd // ""')
|
|
16
|
-
PROJECT_PATH=$(resolve_project_path "${RAW_CWD:-$PWD}")
|
|
17
|
-
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // .sessionId // ""')
|
|
18
|
-
SESSION_ID="${SESSION_ID:-${CLAUDE_CODE_SESSION_ID:-cli}}"
|
|
19
|
-
|
|
20
|
-
# 空 prompt 跳过
|
|
21
|
-
if [ -z "$USER_PROMPT" ]; then
|
|
22
|
-
exit 0
|
|
23
|
-
fi
|
|
24
|
-
|
|
25
|
-
# 构造事件 JSON(jq -n 替代 python3 heredoc)
|
|
26
|
-
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
|
|
27
|
-
EVENT=$(jq -nc \
|
|
28
|
-
--arg hook_type "UserPromptSubmit" \
|
|
29
|
-
--arg timestamp "$TIMESTAMP" \
|
|
30
|
-
--arg session_id "$SESSION_ID" \
|
|
31
|
-
--arg project_path "$PROJECT_PATH" \
|
|
32
|
-
--arg user_prompt "$USER_PROMPT" \
|
|
33
|
-
--arg auth "$AUTH_TOKEN" \
|
|
34
|
-
'{hook_type: $hook_type, timestamp: $timestamp, session_id: $session_id,
|
|
35
|
-
project_path: $project_path, tool_name: "UserPrompt",
|
|
36
|
-
user_prompt: $user_prompt, tool_input: {user_prompt: $user_prompt}, _auth: $auth}')
|
|
37
|
-
|
|
38
|
-
if [ -z "$EVENT" ]; then
|
|
39
|
-
exit 0
|
|
40
|
-
fi
|
|
41
|
-
|
|
42
|
-
# 发送事件(10 秒超时,AI 分析可能需要更多时间)
|
|
43
|
-
# 失败时自动入队(daemon 停机期间事件不丢失)
|
|
44
|
-
send_event_or_enqueue "$EVENT" 10
|
|
45
|
-
RESPONSE="$HOOK_RESPONSE"
|
|
46
|
-
|
|
47
|
-
if [ -n "$RESPONSE" ]; then
|
|
48
|
-
HAS_SYSTEM=$(echo "$RESPONSE" | jq -r 'if .systemMessage and (.systemMessage != "") then "yes" else "no" end')
|
|
49
|
-
HAS_CONTEXT=$(echo "$RESPONSE" | jq -r 'if .additionalContext and (.additionalContext != "") then "yes" else "no" end')
|
|
50
|
-
|
|
51
|
-
if [ "$HAS_SYSTEM" = "yes" ] || [ "$HAS_CONTEXT" = "yes" ]; then
|
|
52
|
-
# stderr 输出摘要到终端
|
|
53
|
-
if [ "$HAS_SYSTEM" = "yes" ]; then
|
|
54
|
-
SM_LEN=$(echo "$RESPONSE" | jq -r '.systemMessage | length')
|
|
55
|
-
printf '\033[36m💉 [Forge] 注入 systemMessage (%s chars)\033[0m\n' "$SM_LEN" >&2
|
|
56
|
-
fi
|
|
57
|
-
if [ "$HAS_CONTEXT" = "yes" ]; then
|
|
58
|
-
CONTEXT=$(echo "$RESPONSE" | jq -r '.additionalContext // ""')
|
|
59
|
-
DISPLAY_LINES=$(printf '%s' "$CONTEXT" | head -3)
|
|
60
|
-
if [ -n "$DISPLAY_LINES" ]; then
|
|
61
|
-
printf '\033[36m💉 [Forge] %s\033[0m\n' "$DISPLAY_LINES" >&2
|
|
62
|
-
fi
|
|
63
|
-
fi
|
|
64
|
-
# stdout 返回给 Claude Code
|
|
65
|
-
echo "$RESPONSE"
|
|
66
|
-
fi
|
|
67
|
-
fi
|
|
68
|
-
|
|
69
|
-
exit 0
|