specweave 0.28.68 → 0.29.1
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.md +3 -2
- package/README.md +19 -2
- package/dist/src/cli/commands/discrepancies.d.ts +89 -0
- package/dist/src/cli/commands/discrepancies.d.ts.map +1 -0
- package/dist/src/cli/commands/discrepancies.js +385 -0
- package/dist/src/cli/commands/discrepancies.js.map +1 -0
- package/dist/src/cli/commands/notifications.d.ts +70 -0
- package/dist/src/cli/commands/notifications.d.ts.map +1 -0
- package/dist/src/cli/commands/notifications.js +236 -0
- package/dist/src/cli/commands/notifications.js.map +1 -0
- package/dist/src/cli/commands/sync-logs.d.ts +54 -0
- package/dist/src/cli/commands/sync-logs.d.ts.map +1 -0
- package/dist/src/cli/commands/sync-logs.js +240 -0
- package/dist/src/cli/commands/sync-logs.js.map +1 -0
- package/dist/src/cli/commands/sync-monitor.d.ts +42 -0
- package/dist/src/cli/commands/sync-monitor.d.ts.map +1 -0
- package/dist/src/cli/commands/sync-monitor.js +191 -0
- package/dist/src/cli/commands/sync-monitor.js.map +1 -0
- package/dist/src/cli/helpers/init/brownfield-analysis.d.ts +45 -0
- package/dist/src/cli/helpers/init/brownfield-analysis.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/brownfield-analysis.js +431 -0
- package/dist/src/cli/helpers/init/brownfield-analysis.js.map +1 -0
- package/dist/src/cli/helpers/init/index.d.ts +1 -0
- package/dist/src/cli/helpers/init/index.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/index.js +2 -0
- package/dist/src/cli/helpers/init/index.js.map +1 -1
- package/dist/src/cli/workers/brownfield-worker.d.ts +66 -0
- package/dist/src/cli/workers/brownfield-worker.d.ts.map +1 -0
- package/dist/src/cli/workers/brownfield-worker.js +417 -0
- package/dist/src/cli/workers/brownfield-worker.js.map +1 -0
- package/dist/src/core/background/brownfield-launcher.d.ts +86 -0
- package/dist/src/core/background/brownfield-launcher.d.ts.map +1 -0
- package/dist/src/core/background/brownfield-launcher.js +295 -0
- package/dist/src/core/background/brownfield-launcher.js.map +1 -0
- package/dist/src/core/background/index.d.ts +2 -0
- package/dist/src/core/background/index.d.ts.map +1 -1
- package/dist/src/core/background/index.js +2 -0
- package/dist/src/core/background/index.js.map +1 -1
- package/dist/src/core/background/types.d.ts +23 -2
- package/dist/src/core/background/types.d.ts.map +1 -1
- package/dist/src/core/config/index.d.ts +1 -0
- package/dist/src/core/config/index.d.ts.map +1 -1
- package/dist/src/core/config/index.js +1 -0
- package/dist/src/core/config/index.js.map +1 -1
- package/dist/src/core/config/types.d.ts +6 -0
- package/dist/src/core/config/types.d.ts.map +1 -1
- package/dist/src/core/config/types.js.map +1 -1
- package/dist/src/core/dashboard/dashboard-data.d.ts +156 -0
- package/dist/src/core/dashboard/dashboard-data.d.ts.map +1 -0
- package/dist/src/core/dashboard/dashboard-data.js +191 -0
- package/dist/src/core/dashboard/dashboard-data.js.map +1 -0
- package/dist/src/core/dashboard/index.d.ts +9 -0
- package/dist/src/core/dashboard/index.d.ts.map +1 -0
- package/dist/src/core/dashboard/index.js +9 -0
- package/dist/src/core/dashboard/index.js.map +1 -0
- package/dist/src/core/discrepancy/analyzers/api-route-analyzer.d.ts +77 -0
- package/dist/src/core/discrepancy/analyzers/api-route-analyzer.d.ts.map +1 -0
- package/dist/src/core/discrepancy/analyzers/api-route-analyzer.js +286 -0
- package/dist/src/core/discrepancy/analyzers/api-route-analyzer.js.map +1 -0
- package/dist/src/core/discrepancy/analyzers/index.d.ts +8 -0
- package/dist/src/core/discrepancy/analyzers/index.d.ts.map +1 -0
- package/dist/src/core/discrepancy/analyzers/index.js +8 -0
- package/dist/src/core/discrepancy/analyzers/index.js.map +1 -0
- package/dist/src/core/discrepancy/analyzers/typescript-analyzer.d.ts +96 -0
- package/dist/src/core/discrepancy/analyzers/typescript-analyzer.d.ts.map +1 -0
- package/dist/src/core/discrepancy/analyzers/typescript-analyzer.js +247 -0
- package/dist/src/core/discrepancy/analyzers/typescript-analyzer.js.map +1 -0
- package/dist/src/core/discrepancy/brownfield-manager.d.ts +88 -0
- package/dist/src/core/discrepancy/brownfield-manager.d.ts.map +1 -0
- package/dist/src/core/discrepancy/brownfield-manager.js +520 -0
- package/dist/src/core/discrepancy/brownfield-manager.js.map +1 -0
- package/dist/src/core/discrepancy/brownfield-types.d.ts +174 -0
- package/dist/src/core/discrepancy/brownfield-types.d.ts.map +1 -0
- package/dist/src/core/discrepancy/brownfield-types.js +11 -0
- package/dist/src/core/discrepancy/brownfield-types.js.map +1 -0
- package/dist/src/core/discrepancy/detector.d.ts +92 -0
- package/dist/src/core/discrepancy/detector.d.ts.map +1 -0
- package/dist/src/core/discrepancy/detector.js +346 -0
- package/dist/src/core/discrepancy/detector.js.map +1 -0
- package/dist/src/core/discrepancy/increment-generator.d.ts +51 -0
- package/dist/src/core/discrepancy/increment-generator.d.ts.map +1 -0
- package/dist/src/core/discrepancy/increment-generator.js +234 -0
- package/dist/src/core/discrepancy/increment-generator.js.map +1 -0
- package/dist/src/core/discrepancy/index.d.ts +18 -0
- package/dist/src/core/discrepancy/index.d.ts.map +1 -0
- package/dist/src/core/discrepancy/index.js +24 -0
- package/dist/src/core/discrepancy/index.js.map +1 -0
- package/dist/src/core/discrepancy/severity-classifier.d.ts +81 -0
- package/dist/src/core/discrepancy/severity-classifier.d.ts.map +1 -0
- package/dist/src/core/discrepancy/severity-classifier.js +289 -0
- package/dist/src/core/discrepancy/severity-classifier.js.map +1 -0
- package/dist/src/core/discrepancy/spec-parser.d.ts +74 -0
- package/dist/src/core/discrepancy/spec-parser.d.ts.map +1 -0
- package/dist/src/core/discrepancy/spec-parser.js +213 -0
- package/dist/src/core/discrepancy/spec-parser.js.map +1 -0
- package/dist/src/core/discrepancy/update-recommender.d.ts +77 -0
- package/dist/src/core/discrepancy/update-recommender.d.ts.map +1 -0
- package/dist/src/core/discrepancy/update-recommender.js +323 -0
- package/dist/src/core/discrepancy/update-recommender.js.map +1 -0
- package/dist/src/core/living-docs/living-docs-sync.d.ts +13 -16
- package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.js +31 -112
- package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
- package/dist/src/core/logs/index.d.ts +10 -0
- package/dist/src/core/logs/index.d.ts.map +1 -0
- package/dist/src/core/logs/index.js +10 -0
- package/dist/src/core/logs/index.js.map +1 -0
- package/dist/src/core/logs/log-aggregator.d.ts +130 -0
- package/dist/src/core/logs/log-aggregator.d.ts.map +1 -0
- package/dist/src/core/logs/log-aggregator.js +206 -0
- package/dist/src/core/logs/log-aggregator.js.map +1 -0
- package/dist/src/core/logs/log-exporter.d.ts +81 -0
- package/dist/src/core/logs/log-exporter.d.ts.map +1 -0
- package/dist/src/core/logs/log-exporter.js +141 -0
- package/dist/src/core/logs/log-exporter.js.map +1 -0
- package/dist/src/core/notifications/command-integration.d.ts +82 -0
- package/dist/src/core/notifications/command-integration.d.ts.map +1 -0
- package/dist/src/core/notifications/command-integration.js +80 -0
- package/dist/src/core/notifications/command-integration.js.map +1 -0
- package/dist/src/core/notifications/index.d.ts +12 -0
- package/dist/src/core/notifications/index.d.ts.map +1 -0
- package/dist/src/core/notifications/index.js +12 -0
- package/dist/src/core/notifications/index.js.map +1 -0
- package/dist/src/core/notifications/notification-display.d.ts +70 -0
- package/dist/src/core/notifications/notification-display.d.ts.map +1 -0
- package/dist/src/core/notifications/notification-display.js +177 -0
- package/dist/src/core/notifications/notification-display.js.map +1 -0
- package/dist/src/core/notifications/notification-manager.d.ts +126 -0
- package/dist/src/core/notifications/notification-manager.d.ts.map +1 -0
- package/dist/src/core/notifications/notification-manager.js +287 -0
- package/dist/src/core/notifications/notification-manager.js.map +1 -0
- package/dist/src/core/notifications/notification-types.d.ts +159 -0
- package/dist/src/core/notifications/notification-types.d.ts.map +1 -0
- package/dist/src/core/notifications/notification-types.js +93 -0
- package/dist/src/core/notifications/notification-types.js.map +1 -0
- package/dist/src/core/scheduler/index.d.ts +11 -0
- package/dist/src/core/scheduler/index.d.ts.map +1 -0
- package/dist/src/core/scheduler/index.js +11 -0
- package/dist/src/core/scheduler/index.js.map +1 -0
- package/dist/src/core/scheduler/job-scheduler.d.ts +179 -0
- package/dist/src/core/scheduler/job-scheduler.d.ts.map +1 -0
- package/dist/src/core/scheduler/job-scheduler.js +282 -0
- package/dist/src/core/scheduler/job-scheduler.js.map +1 -0
- package/dist/src/core/scheduler/schedule-persistence.d.ts +83 -0
- package/dist/src/core/scheduler/schedule-persistence.d.ts.map +1 -0
- package/dist/src/core/scheduler/schedule-persistence.js +180 -0
- package/dist/src/core/scheduler/schedule-persistence.js.map +1 -0
- package/dist/src/core/scheduler/scheduled-job.d.ts +188 -0
- package/dist/src/core/scheduler/scheduled-job.d.ts.map +1 -0
- package/dist/src/core/scheduler/scheduled-job.js +182 -0
- package/dist/src/core/scheduler/scheduled-job.js.map +1 -0
- package/dist/src/core/sync/permission-enforcer.d.ts +206 -0
- package/dist/src/core/sync/permission-enforcer.d.ts.map +1 -0
- package/dist/src/core/sync/permission-enforcer.js +268 -0
- package/dist/src/core/sync/permission-enforcer.js.map +1 -0
- package/dist/src/core/sync/sync-audit-logger.d.ts +217 -0
- package/dist/src/core/sync/sync-audit-logger.d.ts.map +1 -0
- package/dist/src/core/sync/sync-audit-logger.js +327 -0
- package/dist/src/core/sync/sync-audit-logger.js.map +1 -0
- package/dist/src/core/sync/sync-interceptor.d.ts +190 -0
- package/dist/src/core/sync/sync-interceptor.d.ts.map +1 -0
- package/dist/src/core/sync/sync-interceptor.js +224 -0
- package/dist/src/core/sync/sync-interceptor.js.map +1 -0
- package/dist/src/core/types/increment-metadata.d.ts +5 -2
- package/dist/src/core/types/increment-metadata.d.ts.map +1 -1
- package/dist/src/core/types/sync-config.d.ts +267 -0
- package/dist/src/core/types/sync-config.d.ts.map +1 -0
- package/dist/src/core/types/sync-config.js +304 -0
- package/dist/src/core/types/sync-config.js.map +1 -0
- package/dist/src/hooks/index.d.ts +11 -0
- package/dist/src/hooks/index.d.ts.map +1 -0
- package/dist/src/hooks/index.js +11 -0
- package/dist/src/hooks/index.js.map +1 -0
- package/dist/src/hooks/platform.d.ts +125 -0
- package/dist/src/hooks/platform.d.ts.map +1 -0
- package/dist/src/hooks/platform.js +325 -0
- package/dist/src/hooks/platform.js.map +1 -0
- package/dist/src/hooks/processor.d.ts +20 -0
- package/dist/src/hooks/processor.d.ts.map +1 -0
- package/dist/src/hooks/processor.js +317 -0
- package/dist/src/hooks/processor.js.map +1 -0
- package/dist/src/hooks/scheduler-startup.d.ts +19 -0
- package/dist/src/hooks/scheduler-startup.d.ts.map +1 -0
- package/dist/src/hooks/scheduler-startup.js +92 -0
- package/dist/src/hooks/scheduler-startup.js.map +1 -0
- package/dist/src/hooks/session-start.d.ts +16 -0
- package/dist/src/hooks/session-start.d.ts.map +1 -0
- package/dist/src/hooks/session-start.js +92 -0
- package/dist/src/hooks/session-start.js.map +1 -0
- package/dist/src/importers/duplicate-detector.d.ts +13 -2
- package/dist/src/importers/duplicate-detector.d.ts.map +1 -1
- package/dist/src/importers/duplicate-detector.js +21 -2
- package/dist/src/importers/duplicate-detector.js.map +1 -1
- package/dist/src/importers/item-converter.d.ts +41 -2
- package/dist/src/importers/item-converter.d.ts.map +1 -1
- package/dist/src/importers/item-converter.js +225 -38
- package/dist/src/importers/item-converter.js.map +1 -1
- package/dist/src/living-docs/fs-id-allocator.d.ts +7 -0
- package/dist/src/living-docs/fs-id-allocator.d.ts.map +1 -1
- package/dist/src/living-docs/fs-id-allocator.js +30 -4
- package/dist/src/living-docs/fs-id-allocator.js.map +1 -1
- package/dist/src/sync/ado-sync-wrapper.d.ts +137 -0
- package/dist/src/sync/ado-sync-wrapper.d.ts.map +1 -0
- package/dist/src/sync/ado-sync-wrapper.js +148 -0
- package/dist/src/sync/ado-sync-wrapper.js.map +1 -0
- package/dist/src/sync/github-sync-wrapper.d.ts +195 -0
- package/dist/src/sync/github-sync-wrapper.d.ts.map +1 -0
- package/dist/src/sync/github-sync-wrapper.js +220 -0
- package/dist/src/sync/github-sync-wrapper.js.map +1 -0
- package/dist/src/sync/jira-sync-wrapper.d.ts +155 -0
- package/dist/src/sync/jira-sync-wrapper.d.ts.map +1 -0
- package/dist/src/sync/jira-sync-wrapper.js +175 -0
- package/dist/src/sync/jira-sync-wrapper.js.map +1 -0
- package/dist/src/utils/feature-id-derivation.d.ts +58 -0
- package/dist/src/utils/feature-id-derivation.d.ts.map +1 -0
- package/dist/src/utils/feature-id-derivation.js +77 -0
- package/dist/src/utils/feature-id-derivation.js.map +1 -0
- package/package.json +3 -1
- package/plugins/specweave/commands/specweave-discrepancies.md +141 -0
- package/plugins/specweave/commands/specweave-discrepancy-to-increment.md +160 -0
- package/plugins/specweave/commands/specweave-jobs.md +45 -2
- package/plugins/specweave/commands/specweave-notifications.md +92 -0
- package/plugins/specweave/commands/specweave-sync-logs.md +131 -0
- package/plugins/specweave/commands/specweave-sync-monitor.md +57 -0
- package/plugins/specweave/hooks/hooks.json +3 -3
- package/plugins/specweave/hooks/lib/scheduler-startup.sh +72 -0
- package/plugins/specweave/hooks/universal/dispatcher.mjs +246 -0
- package/plugins/specweave/hooks/universal/session-start.cmd +16 -0
- package/plugins/specweave/hooks/universal/session-start.ps1 +16 -0
- package/plugins/specweave/hooks/v2/dispatchers/session-start.sh +14 -5
- package/plugins/specweave/lib/vendor/core/types/increment-metadata.d.ts +5 -2
- package/plugins/specweave/skills/discrepancy-viewer.md +154 -0
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +46 -0
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +69 -0
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.d.ts +0 -26
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.d.ts.map +0 -1
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.js +0 -249
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.js.map +0 -1
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.d.ts +0 -28
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.d.ts.map +0 -1
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js +0 -156
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js.map +0 -1
- package/dist/src/core/sync/bidirectional-engine.d.ts +0 -119
- package/dist/src/core/sync/bidirectional-engine.d.ts.map +0 -1
- package/dist/src/core/sync/bidirectional-engine.js +0 -359
- package/dist/src/core/sync/bidirectional-engine.js.map +0 -1
- package/dist/src/core/sync/conflict-resolver.d.ts +0 -66
- package/dist/src/core/sync/conflict-resolver.d.ts.map +0 -1
- package/dist/src/core/sync/conflict-resolver.js +0 -108
- package/dist/src/core/sync/conflict-resolver.js.map +0 -1
- package/dist/src/core/sync/enhanced-content-builder.d.ts +0 -55
- package/dist/src/core/sync/enhanced-content-builder.d.ts.map +0 -1
- package/dist/src/core/sync/enhanced-content-builder.js +0 -203
- package/dist/src/core/sync/enhanced-content-builder.js.map +0 -1
- package/dist/src/core/sync/folder-mapper.d.ts +0 -71
- package/dist/src/core/sync/folder-mapper.d.ts.map +0 -1
- package/dist/src/core/sync/folder-mapper.js +0 -203
- package/dist/src/core/sync/folder-mapper.js.map +0 -1
- package/dist/src/core/sync/label-detector.d.ts +0 -66
- package/dist/src/core/sync/label-detector.d.ts.map +0 -1
- package/dist/src/core/sync/label-detector.js +0 -224
- package/dist/src/core/sync/label-detector.js.map +0 -1
- package/dist/src/core/sync/performance-optimizer.d.ts +0 -153
- package/dist/src/core/sync/performance-optimizer.d.ts.map +0 -1
- package/dist/src/core/sync/performance-optimizer.js +0 -220
- package/dist/src/core/sync/performance-optimizer.js.map +0 -1
- package/dist/src/core/sync/profile-selector.d.ts +0 -52
- package/dist/src/core/sync/profile-selector.d.ts.map +0 -1
- package/dist/src/core/sync/profile-selector.js +0 -179
- package/dist/src/core/sync/profile-selector.js.map +0 -1
- package/dist/src/core/sync/profile-validator.d.ts +0 -52
- package/dist/src/core/sync/profile-validator.d.ts.map +0 -1
- package/dist/src/core/sync/profile-validator.js +0 -170
- package/dist/src/core/sync/profile-validator.js.map +0 -1
- package/dist/src/core/sync/rate-limiter.d.ts +0 -116
- package/dist/src/core/sync/rate-limiter.d.ts.map +0 -1
- package/dist/src/core/sync/rate-limiter.js +0 -308
- package/dist/src/core/sync/rate-limiter.js.map +0 -1
- package/dist/src/core/sync/retry-handler.d.ts +0 -98
- package/dist/src/core/sync/retry-handler.d.ts.map +0 -1
- package/dist/src/core/sync/retry-handler.js +0 -196
- package/dist/src/core/sync/retry-handler.js.map +0 -1
- package/dist/src/core/sync/retry-logic.d.ts +0 -64
- package/dist/src/core/sync/retry-logic.d.ts.map +0 -1
- package/dist/src/core/sync/retry-logic.js +0 -165
- package/dist/src/core/sync/retry-logic.js.map +0 -1
- package/dist/src/core/sync/status-cache.d.ts +0 -91
- package/dist/src/core/sync/status-cache.d.ts.map +0 -1
- package/dist/src/core/sync/status-cache.js +0 -140
- package/dist/src/core/sync/status-cache.js.map +0 -1
- package/dist/src/core/sync/status-mapper.d.ts +0 -69
- package/dist/src/core/sync/status-mapper.d.ts.map +0 -1
- package/dist/src/core/sync/status-mapper.js +0 -90
- package/dist/src/core/sync/status-mapper.js.map +0 -1
- package/dist/src/core/sync/status-sync-engine.d.ts +0 -162
- package/dist/src/core/sync/status-sync-engine.d.ts.map +0 -1
- package/dist/src/core/sync/status-sync-engine.js +0 -347
- package/dist/src/core/sync/status-sync-engine.js.map +0 -1
- package/dist/src/core/sync/sync-event-logger.d.ts +0 -113
- package/dist/src/core/sync/sync-event-logger.d.ts.map +0 -1
- package/dist/src/core/sync/sync-event-logger.js +0 -141
- package/dist/src/core/sync/sync-event-logger.js.map +0 -1
- package/dist/src/core/sync/time-range-selector.d.ts +0 -48
- package/dist/src/core/sync/time-range-selector.d.ts.map +0 -1
- package/dist/src/core/sync/time-range-selector.js +0 -224
- package/dist/src/core/sync/time-range-selector.js.map +0 -1
- package/dist/src/core/sync/types.d.ts +0 -52
- package/dist/src/core/sync/types.d.ts.map +0 -1
- package/dist/src/core/sync/types.js +0 -5
- package/dist/src/core/sync/types.js.map +0 -1
- package/dist/src/core/sync/workflow-detector.d.ts +0 -95
- package/dist/src/core/sync/workflow-detector.d.ts.map +0 -1
- package/dist/src/core/sync/workflow-detector.js +0 -175
- package/dist/src/core/sync/workflow-detector.js.map +0 -1
- package/plugins/specweave-github/lib/enhanced-github-sync.js +0 -220
- package/plugins/specweave-github/lib/enhanced-github-sync.ts +0 -322
- package/plugins/specweave-jira/lib/enhanced-jira-sync.js +0 -134
- package/plugins/specweave-jira/lib/enhanced-jira-sync.ts +0 -196
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Background Event Processor (Cross-Platform)
|
|
4
|
+
*
|
|
5
|
+
* Processes queued events asynchronously, routes to specialized handlers.
|
|
6
|
+
* Works on Windows, macOS, and Linux without WSL requirement.
|
|
7
|
+
*
|
|
8
|
+
* Usage: node processor.js [--daemon]
|
|
9
|
+
*
|
|
10
|
+
* Event routing:
|
|
11
|
+
* - increment.created/done/archived/reopened -> living-specs-handler
|
|
12
|
+
* - user-story.completed/reopened -> status-line-handler
|
|
13
|
+
* - task.updated/spec.updated -> living-docs-handler (legacy)
|
|
14
|
+
*
|
|
15
|
+
* Self-terminates after 60s of idle
|
|
16
|
+
*
|
|
17
|
+
* @module hooks/processor
|
|
18
|
+
*/
|
|
19
|
+
import * as path from 'path';
|
|
20
|
+
import * as fs from 'fs';
|
|
21
|
+
import { spawn } from 'child_process';
|
|
22
|
+
import { findProjectRoot, FileLock, appendLog, isWindows, } from './platform.js';
|
|
23
|
+
// Configuration
|
|
24
|
+
const IDLE_TIMEOUT = 60; // seconds before self-termination
|
|
25
|
+
const HANDLER_TIMEOUT = 30000; // ms per handler call
|
|
26
|
+
const STALE_LOCK_SECONDS = 300; // 5 minutes
|
|
27
|
+
/**
|
|
28
|
+
* Initialize processor state
|
|
29
|
+
*/
|
|
30
|
+
function initState(projectRoot, daemonMode) {
|
|
31
|
+
const specweavePath = path.join(projectRoot, '.specweave');
|
|
32
|
+
return {
|
|
33
|
+
projectRoot,
|
|
34
|
+
queueDir: path.join(specweavePath, 'state', 'event-queue'),
|
|
35
|
+
handlerDir: path.join(projectRoot, 'plugins', 'specweave', 'hooks', 'v2', 'handlers'),
|
|
36
|
+
logFile: path.join(specweavePath, 'logs', 'processor.log'),
|
|
37
|
+
pidFile: path.join(specweavePath, 'state', '.processor.pid'),
|
|
38
|
+
lock: new FileLock(path.join(specweavePath, 'state', '.processor')),
|
|
39
|
+
daemonMode,
|
|
40
|
+
idleCount: 0,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Dequeue an event from the queue directory
|
|
45
|
+
*/
|
|
46
|
+
function dequeue(state) {
|
|
47
|
+
try {
|
|
48
|
+
if (!fs.existsSync(state.queueDir)) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
// Get oldest event file
|
|
52
|
+
const files = fs.readdirSync(state.queueDir)
|
|
53
|
+
.filter((f) => f.endsWith('.json'))
|
|
54
|
+
.sort(); // Oldest first (timestamp-based naming)
|
|
55
|
+
if (files.length === 0) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const eventFile = path.join(state.queueDir, files[0]);
|
|
59
|
+
const content = fs.readFileSync(eventFile, 'utf-8');
|
|
60
|
+
// Delete after reading
|
|
61
|
+
fs.unlinkSync(eventFile);
|
|
62
|
+
return JSON.parse(content);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Run a handler script with timeout
|
|
70
|
+
*/
|
|
71
|
+
async function runHandler(handlerPath, eventType, eventData, state) {
|
|
72
|
+
if (!fs.existsSync(handlerPath)) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
return new Promise((resolve) => {
|
|
76
|
+
let child;
|
|
77
|
+
let timeoutId;
|
|
78
|
+
const cleanup = () => {
|
|
79
|
+
clearTimeout(timeoutId);
|
|
80
|
+
try {
|
|
81
|
+
child?.kill();
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// Ignore kill errors
|
|
85
|
+
}
|
|
86
|
+
resolve();
|
|
87
|
+
};
|
|
88
|
+
try {
|
|
89
|
+
if (isWindows()) {
|
|
90
|
+
// On Windows, run bash scripts via Git Bash if available, or skip
|
|
91
|
+
const gitBashPath = findGitBash();
|
|
92
|
+
if (gitBashPath) {
|
|
93
|
+
child = spawn(gitBashPath, [handlerPath, eventType, eventData], {
|
|
94
|
+
cwd: state.projectRoot,
|
|
95
|
+
stdio: 'ignore',
|
|
96
|
+
windowsHide: true,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// No Git Bash - try to run via WSL if available (FALLBACK only)
|
|
101
|
+
const wslPath = path.join(process.env.SYSTEMROOT || 'C:\\Windows', 'System32', 'wsl.exe');
|
|
102
|
+
if (fs.existsSync(wslPath)) {
|
|
103
|
+
// Convert Windows path to WSL path:
|
|
104
|
+
// 1. Backslashes to forward slashes: C:\path\file -> C:/path/file
|
|
105
|
+
// 2. Drive letter to /mnt/: C:/path/file -> /mnt/c/path/file
|
|
106
|
+
// NOTE: Handle BOTH uppercase and lowercase drive letters (C: and c:)
|
|
107
|
+
const wslHandlerPath = handlerPath
|
|
108
|
+
.replace(/\\/g, '/')
|
|
109
|
+
.replace(/^([A-Za-z]):/, (_, drive) => `/mnt/${drive.toLowerCase()}`);
|
|
110
|
+
child = spawn('wsl', ['bash', wslHandlerPath, eventType, eventData], {
|
|
111
|
+
cwd: state.projectRoot,
|
|
112
|
+
stdio: 'ignore',
|
|
113
|
+
windowsHide: true,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
// No bash available - skip handler
|
|
118
|
+
appendLog(state.logFile, `Skipped handler (no bash): ${path.basename(handlerPath)}`);
|
|
119
|
+
resolve();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
// POSIX - run directly
|
|
126
|
+
child = spawn('bash', [handlerPath, eventType, eventData], {
|
|
127
|
+
cwd: state.projectRoot,
|
|
128
|
+
stdio: 'ignore',
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
// Set timeout
|
|
132
|
+
timeoutId = setTimeout(() => {
|
|
133
|
+
appendLog(state.logFile, `Handler timeout: ${path.basename(handlerPath)}`);
|
|
134
|
+
cleanup();
|
|
135
|
+
}, HANDLER_TIMEOUT);
|
|
136
|
+
child.on('exit', cleanup);
|
|
137
|
+
child.on('error', cleanup);
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
appendLog(state.logFile, `Handler error: ${err}`);
|
|
141
|
+
resolve();
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Cached Git Bash path (computed once at module load)
|
|
147
|
+
* undefined = not yet checked, null = checked and not found, string = path found
|
|
148
|
+
*/
|
|
149
|
+
let gitBashPathCache = undefined;
|
|
150
|
+
/**
|
|
151
|
+
* Find Git Bash on Windows (with caching)
|
|
152
|
+
*
|
|
153
|
+
* Caches the result to avoid repeated filesystem checks.
|
|
154
|
+
* On Windows, checks common Git installation locations.
|
|
155
|
+
*/
|
|
156
|
+
function findGitBash() {
|
|
157
|
+
// Return cached result if available
|
|
158
|
+
if (gitBashPathCache !== undefined) {
|
|
159
|
+
return gitBashPathCache;
|
|
160
|
+
}
|
|
161
|
+
if (!isWindows()) {
|
|
162
|
+
gitBashPathCache = null;
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
const possiblePaths = [
|
|
166
|
+
// Standard Git for Windows installation paths
|
|
167
|
+
path.join(process.env.PROGRAMFILES || 'C:\\Program Files', 'Git', 'bin', 'bash.exe'),
|
|
168
|
+
path.join(process.env['PROGRAMFILES(X86)'] || 'C:\\Program Files (x86)', 'Git', 'bin', 'bash.exe'),
|
|
169
|
+
// User-specific installation
|
|
170
|
+
path.join(process.env.LOCALAPPDATA || '', 'Programs', 'Git', 'bin', 'bash.exe'),
|
|
171
|
+
// Portable Git / custom installation
|
|
172
|
+
'C:\\Git\\bin\\bash.exe',
|
|
173
|
+
];
|
|
174
|
+
for (const bashPath of possiblePaths) {
|
|
175
|
+
if (bashPath && fs.existsSync(bashPath)) {
|
|
176
|
+
gitBashPathCache = bashPath;
|
|
177
|
+
return bashPath;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
gitBashPathCache = null;
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Process a single event
|
|
185
|
+
*/
|
|
186
|
+
async function processEvent(event, state) {
|
|
187
|
+
const { type, data } = event;
|
|
188
|
+
appendLog(state.logFile, `Processing: ${type} (${data})`);
|
|
189
|
+
const handlers = [
|
|
190
|
+
// EDA Event Routing (new architecture)
|
|
191
|
+
{
|
|
192
|
+
pattern: /^increment\.(created|done|archived|reopened)$/,
|
|
193
|
+
handlers: ['living-specs-handler.sh', 'status-line-handler.sh'],
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
pattern: /^user-story\.(completed|reopened)$/,
|
|
197
|
+
handlers: ['status-line-handler.sh'],
|
|
198
|
+
},
|
|
199
|
+
// Legacy event routing (backward compat)
|
|
200
|
+
{
|
|
201
|
+
pattern: /^(task|spec)\.updated$/,
|
|
202
|
+
handlers: ['living-docs-handler.sh', 'ac-validation-handler.sh'],
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
pattern: /^metadata\.changed$/,
|
|
206
|
+
handlers: ['github-sync-handler.sh'],
|
|
207
|
+
},
|
|
208
|
+
];
|
|
209
|
+
for (const route of handlers) {
|
|
210
|
+
if (route.pattern.test(type)) {
|
|
211
|
+
for (const handlerName of route.handlers) {
|
|
212
|
+
const handlerPath = path.join(state.handlerDir, handlerName);
|
|
213
|
+
await runHandler(handlerPath, type, data, state);
|
|
214
|
+
}
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
appendLog(state.logFile, `Unknown event type: ${type}`);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Main processing loop
|
|
222
|
+
*/
|
|
223
|
+
async function mainLoop(state) {
|
|
224
|
+
appendLog(state.logFile, `Processor started (PID: ${process.pid}, timeout: ${IDLE_TIMEOUT}s)`);
|
|
225
|
+
// Write PID file
|
|
226
|
+
fs.mkdirSync(path.dirname(state.pidFile), { recursive: true });
|
|
227
|
+
fs.writeFileSync(state.pidFile, String(process.pid), 'utf-8');
|
|
228
|
+
// Ensure queue directory exists
|
|
229
|
+
fs.mkdirSync(state.queueDir, { recursive: true });
|
|
230
|
+
while (true) {
|
|
231
|
+
const event = dequeue(state);
|
|
232
|
+
if (event) {
|
|
233
|
+
state.idleCount = 0;
|
|
234
|
+
await processEvent(event, state);
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
state.idleCount++;
|
|
238
|
+
if (state.idleCount >= IDLE_TIMEOUT) {
|
|
239
|
+
appendLog(state.logFile, `Idle timeout (${IDLE_TIMEOUT}s), exiting`);
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
// In non-daemon mode, exit after 3s of idle
|
|
243
|
+
if (!state.daemonMode && state.idleCount >= 3) {
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
// Wait 1 second before checking again
|
|
247
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// Cleanup
|
|
251
|
+
state.lock.release();
|
|
252
|
+
try {
|
|
253
|
+
fs.unlinkSync(state.pidFile);
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
// Ignore cleanup errors
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Main entry point
|
|
261
|
+
*/
|
|
262
|
+
async function main() {
|
|
263
|
+
// Check if hooks disabled
|
|
264
|
+
if (process.env.SPECWEAVE_DISABLE_HOOKS === '1') {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
// Parse arguments
|
|
268
|
+
const daemonMode = process.argv.includes('--daemon');
|
|
269
|
+
// Find project root
|
|
270
|
+
const projectRoot = findProjectRoot();
|
|
271
|
+
if (!projectRoot) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
// Initialize state
|
|
275
|
+
const state = initState(projectRoot, daemonMode);
|
|
276
|
+
// Try to acquire lock
|
|
277
|
+
if (!state.lock.acquire(STALE_LOCK_SECONDS)) {
|
|
278
|
+
// Another processor is running
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
// Check PID file for extra safety
|
|
282
|
+
if (fs.existsSync(state.pidFile)) {
|
|
283
|
+
try {
|
|
284
|
+
const oldPid = parseInt(fs.readFileSync(state.pidFile, 'utf-8').trim(), 10);
|
|
285
|
+
if (!isNaN(oldPid)) {
|
|
286
|
+
try {
|
|
287
|
+
process.kill(oldPid, 0);
|
|
288
|
+
// Process is running - exit
|
|
289
|
+
state.lock.release();
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
// Process not running - continue
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
catch {
|
|
298
|
+
// Ignore PID file errors
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
// Run main loop
|
|
302
|
+
try {
|
|
303
|
+
await mainLoop(state);
|
|
304
|
+
}
|
|
305
|
+
catch (err) {
|
|
306
|
+
appendLog(state.logFile, `Processor error: ${err}`);
|
|
307
|
+
}
|
|
308
|
+
finally {
|
|
309
|
+
state.lock.release();
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
// Run if executed directly
|
|
313
|
+
main().catch((err) => {
|
|
314
|
+
console.error('Processor fatal error:', err);
|
|
315
|
+
process.exit(1);
|
|
316
|
+
});
|
|
317
|
+
//# sourceMappingURL=processor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"processor.js","sourceRoot":"","sources":["../../../src/hooks/processor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Scheduler Startup Check (Cross-Platform)
|
|
4
|
+
*
|
|
5
|
+
* Checks for due scheduled jobs on session start.
|
|
6
|
+
* Returns system message if jobs are ready to run.
|
|
7
|
+
*
|
|
8
|
+
* @module hooks/scheduler-startup
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Check for due scheduled jobs
|
|
12
|
+
*
|
|
13
|
+
* @param projectRoot - Project root directory
|
|
14
|
+
* @returns Object with systemMessage if jobs are due
|
|
15
|
+
*/
|
|
16
|
+
export declare function checkScheduledJobs(projectRoot?: string): Promise<{
|
|
17
|
+
systemMessage?: string;
|
|
18
|
+
} | null>;
|
|
19
|
+
//# sourceMappingURL=scheduler-startup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler-startup.d.ts","sourceRoot":"","sources":["../../../src/hooks/scheduler-startup.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;AA4CH;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CA0D5C"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Scheduler Startup Check (Cross-Platform)
|
|
4
|
+
*
|
|
5
|
+
* Checks for due scheduled jobs on session start.
|
|
6
|
+
* Returns system message if jobs are ready to run.
|
|
7
|
+
*
|
|
8
|
+
* @module hooks/scheduler-startup
|
|
9
|
+
*/
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
import * as fs from 'fs';
|
|
12
|
+
import { findProjectRoot, readJsonSafe, appendLog, outputHookResult } from './platform.js';
|
|
13
|
+
/**
|
|
14
|
+
* Check for due scheduled jobs
|
|
15
|
+
*
|
|
16
|
+
* @param projectRoot - Project root directory
|
|
17
|
+
* @returns Object with systemMessage if jobs are due
|
|
18
|
+
*/
|
|
19
|
+
export async function checkScheduledJobs(projectRoot) {
|
|
20
|
+
const root = projectRoot || findProjectRoot();
|
|
21
|
+
if (!root) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
const configFile = path.join(root, '.specweave', 'config.json');
|
|
25
|
+
const scheduledJobsFile = path.join(root, '.specweave', 'state', 'scheduled-jobs.json');
|
|
26
|
+
const logFile = path.join(root, '.specweave', 'logs', 'scheduler.log');
|
|
27
|
+
// Check if scheduler is enabled
|
|
28
|
+
const config = readJsonSafe(configFile);
|
|
29
|
+
const schedulerEnabled = config?.sync?.orchestration?.scheduler?.enabled ?? false;
|
|
30
|
+
if (!schedulerEnabled) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
// Check if scheduled jobs file exists
|
|
34
|
+
if (!fs.existsSync(scheduledJobsFile)) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
// Read and parse scheduled jobs
|
|
38
|
+
const jobsData = readJsonSafe(scheduledJobsFile);
|
|
39
|
+
if (!jobsData?.jobs || !Array.isArray(jobsData.jobs)) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
// Find due jobs
|
|
43
|
+
const now = Date.now();
|
|
44
|
+
const dueJobs = jobsData.jobs.filter((job) => {
|
|
45
|
+
// Job must be enabled and idle
|
|
46
|
+
if (!job.schedule.enabled || job.status !== 'idle') {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
// No nextRun means it should run now
|
|
50
|
+
if (!job.schedule.nextRun) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
// Check if nextRun has passed
|
|
54
|
+
const nextRunTime = new Date(job.schedule.nextRun).getTime();
|
|
55
|
+
return nextRunTime <= now;
|
|
56
|
+
});
|
|
57
|
+
if (dueJobs.length === 0) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
// Log due jobs
|
|
61
|
+
const dueJobIds = dueJobs.map((j) => j.id);
|
|
62
|
+
appendLog(logFile, `Due sync jobs: ${JSON.stringify(dueJobIds)}`);
|
|
63
|
+
return {
|
|
64
|
+
systemMessage: `Scheduled sync jobs ready to run. Use /specweave:sync-now to execute.`,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Main entry point (when run directly)
|
|
69
|
+
*/
|
|
70
|
+
async function main() {
|
|
71
|
+
// Check if hooks disabled
|
|
72
|
+
if (process.env.SPECWEAVE_DISABLE_HOOKS === '1') {
|
|
73
|
+
outputHookResult({ continue: true });
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const result = await checkScheduledJobs();
|
|
77
|
+
if (result?.systemMessage) {
|
|
78
|
+
outputHookResult({ continue: true, systemMessage: result.systemMessage });
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
outputHookResult({ continue: true });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Run if executed directly
|
|
85
|
+
const isMainModule = import.meta.url === `file://${process.argv[1]}`;
|
|
86
|
+
if (isMainModule) {
|
|
87
|
+
main().catch((err) => {
|
|
88
|
+
console.error(JSON.stringify({ continue: true, error: String(err) }));
|
|
89
|
+
process.exit(0);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=scheduler-startup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler-startup.js","sourceRoot":"","sources":["../../../src/hooks/scheduler-startup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Session Start Hook (Cross-Platform)
|
|
4
|
+
*
|
|
5
|
+
* Launches background processor on session start.
|
|
6
|
+
* Works on Windows, macOS, and Linux without WSL requirement.
|
|
7
|
+
*
|
|
8
|
+
* Windows Support Strategy:
|
|
9
|
+
* 1. Git Bash (PRIMARY - required for production use)
|
|
10
|
+
* 2. WSL (FALLBACK - for development/emergency only)
|
|
11
|
+
* 3. Graceful degradation (skip bash handlers if neither available)
|
|
12
|
+
*
|
|
13
|
+
* @module hooks/session-start
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=session-start.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-start.d.ts","sourceRoot":"","sources":["../../../src/hooks/session-start.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;GAYG"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Session Start Hook (Cross-Platform)
|
|
4
|
+
*
|
|
5
|
+
* Launches background processor on session start.
|
|
6
|
+
* Works on Windows, macOS, and Linux without WSL requirement.
|
|
7
|
+
*
|
|
8
|
+
* Windows Support Strategy:
|
|
9
|
+
* 1. Git Bash (PRIMARY - required for production use)
|
|
10
|
+
* 2. WSL (FALLBACK - for development/emergency only)
|
|
11
|
+
* 3. Graceful degradation (skip bash handlers if neither available)
|
|
12
|
+
*
|
|
13
|
+
* @module hooks/session-start
|
|
14
|
+
*/
|
|
15
|
+
import * as path from 'path';
|
|
16
|
+
import * as fs from 'fs';
|
|
17
|
+
import { fileURLToPath } from 'url';
|
|
18
|
+
import { findProjectRoot, spawnNodeBackground, outputHookResult, consumeStdin, appendLog, } from './platform.js';
|
|
19
|
+
// Proper cross-platform URL-to-path conversion
|
|
20
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
21
|
+
const __dirname = path.dirname(__filename);
|
|
22
|
+
/**
|
|
23
|
+
* Check if hooks are disabled
|
|
24
|
+
*/
|
|
25
|
+
function isHooksDisabled() {
|
|
26
|
+
return process.env.SPECWEAVE_DISABLE_HOOKS === '1';
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Main entry point
|
|
30
|
+
*/
|
|
31
|
+
async function main() {
|
|
32
|
+
// Check if hooks disabled
|
|
33
|
+
if (isHooksDisabled()) {
|
|
34
|
+
outputHookResult({ continue: true });
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
// Find project root
|
|
38
|
+
const projectRoot = findProjectRoot();
|
|
39
|
+
if (!projectRoot) {
|
|
40
|
+
outputHookResult({ continue: true });
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
// Consume stdin (required by hook protocol)
|
|
44
|
+
await consumeStdin();
|
|
45
|
+
const logFile = path.join(projectRoot, '.specweave', 'logs', 'session-start.log');
|
|
46
|
+
const distHooksDir = path.join(projectRoot, 'node_modules', 'specweave', 'dist', 'hooks');
|
|
47
|
+
// Check for processor script
|
|
48
|
+
// First try: built hooks in dist (production)
|
|
49
|
+
// Fallback: development mode - hooks relative to this file
|
|
50
|
+
let processorScript = path.join(distHooksDir, 'processor.js');
|
|
51
|
+
if (!fs.existsSync(processorScript)) {
|
|
52
|
+
// Development mode - use fileURLToPath for correct cross-platform path
|
|
53
|
+
processorScript = path.join(__dirname, 'processor.js');
|
|
54
|
+
}
|
|
55
|
+
// Launch processor in background (daemon mode)
|
|
56
|
+
if (fs.existsSync(processorScript)) {
|
|
57
|
+
const child = spawnNodeBackground(processorScript, ['--daemon'], projectRoot);
|
|
58
|
+
if (child) {
|
|
59
|
+
appendLog(logFile, `Processor started (PID: ${child.pid})`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Check for scheduler startup
|
|
63
|
+
let schedulerScript = path.join(distHooksDir, 'scheduler-startup.js');
|
|
64
|
+
if (!fs.existsSync(schedulerScript)) {
|
|
65
|
+
// Development mode - use fileURLToPath for correct cross-platform path
|
|
66
|
+
schedulerScript = path.join(__dirname, 'scheduler-startup.js');
|
|
67
|
+
}
|
|
68
|
+
// Run scheduler check (synchronously for hook response)
|
|
69
|
+
if (fs.existsSync(schedulerScript)) {
|
|
70
|
+
try {
|
|
71
|
+
// Dynamic import to run scheduler check
|
|
72
|
+
const schedulerModule = await import(schedulerScript);
|
|
73
|
+
if (typeof schedulerModule.checkScheduledJobs === 'function') {
|
|
74
|
+
const result = await schedulerModule.checkScheduledJobs(projectRoot);
|
|
75
|
+
if (result && result.systemMessage) {
|
|
76
|
+
outputHookResult({ continue: true, systemMessage: result.systemMessage });
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
appendLog(logFile, `Scheduler check failed: ${err}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
outputHookResult({ continue: true });
|
|
86
|
+
}
|
|
87
|
+
// Run main
|
|
88
|
+
main().catch((err) => {
|
|
89
|
+
console.error(JSON.stringify({ continue: true, error: String(err) }));
|
|
90
|
+
process.exit(0); // Don't block Claude Code on hook errors
|
|
91
|
+
});
|
|
92
|
+
//# sourceMappingURL=session-start.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-start.js","sourceRoot":"","sources":["../../../src/hooks/session-start.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
/**
|
|
8
8
|
* External ID reference found in living docs
|
|
9
|
+
* CRITICAL (2025-12-01): Includes parent tracking for re-import hierarchy updates
|
|
9
10
|
*/
|
|
10
11
|
export interface ExternalIdReference {
|
|
11
12
|
/** User Story ID (e.g., US-001E) */
|
|
@@ -16,6 +17,14 @@ export interface ExternalIdReference {
|
|
|
16
17
|
externalId: string;
|
|
17
18
|
/** External platform (github, jira, ado) */
|
|
18
19
|
platform?: string;
|
|
20
|
+
/** Feature ID this item belongs to (e.g., FS-001E) */
|
|
21
|
+
featureId?: string;
|
|
22
|
+
/** Parent work item ID in external tool (e.g., ADO-123) */
|
|
23
|
+
parentId?: string;
|
|
24
|
+
/** Whether this item was imported as orphan (no parent) */
|
|
25
|
+
isOrphan?: boolean;
|
|
26
|
+
/** ADO work item type (e.g., User Story, Bug) */
|
|
27
|
+
adoWorkItemType?: string;
|
|
19
28
|
}
|
|
20
29
|
/**
|
|
21
30
|
* Options for duplicate detection
|
|
@@ -71,10 +80,12 @@ export declare class DuplicateDetector {
|
|
|
71
80
|
*/
|
|
72
81
|
private findUserStoryFiles;
|
|
73
82
|
/**
|
|
74
|
-
* Extract external_id from a User Story file
|
|
83
|
+
* Extract external_id and parent tracking info from a User Story file
|
|
84
|
+
*
|
|
85
|
+
* CRITICAL (2025-12-01): Also extracts parent info for re-import hierarchy updates
|
|
75
86
|
*
|
|
76
87
|
* @param filePath - Path to User Story markdown file
|
|
77
|
-
* @returns External ID reference if found, null otherwise
|
|
88
|
+
* @returns External ID reference with parent info if found, null otherwise
|
|
78
89
|
*/
|
|
79
90
|
private extractExternalIdFromFile;
|
|
80
91
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"duplicate-detector.d.ts","sourceRoot":"","sources":["../../../src/importers/duplicate-detector.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH
|
|
1
|
+
{"version":3,"file":"duplicate-detector.d.ts","sourceRoot":"","sources":["../../../src/importers/duplicate-detector.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IAEb,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,CAAC;IAEjB,wBAAwB;IACxB,UAAU,EAAE,MAAM,CAAC;IAEnB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,iDAAiD;IACjD,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;IAEjB,yCAAyC;IACzC,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;GAKG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,OAAO,CAA2B;IAC1C,OAAO,CAAC,KAAK,CAAiD;gBAElD,OAAO,EAAE,wBAAwB;IAO7C;;;;;OAKG;IACG,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKnE;;;;;OAKG;IACG,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;IAUtF;;;;OAIG;IACG,iBAAiB,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAIpE;;OAEG;YACW,kBAAkB;IA4BhC;;;;;;;;OAQG;YACW,kBAAkB;IAYhC;;;;;;;OAOG;YACW,yBAAyB;IAoDvC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAiB3B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,mBAAmB;IAY3B;;OAEG;IACH,UAAU,IAAI,IAAI;IAIlB;;OAEG;IACH,aAAa,IAAI;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;CAMpD;AAED;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC,CAGlB"}
|
|
@@ -97,10 +97,12 @@ export class DuplicateDetector {
|
|
|
97
97
|
return files;
|
|
98
98
|
}
|
|
99
99
|
/**
|
|
100
|
-
* Extract external_id from a User Story file
|
|
100
|
+
* Extract external_id and parent tracking info from a User Story file
|
|
101
|
+
*
|
|
102
|
+
* CRITICAL (2025-12-01): Also extracts parent info for re-import hierarchy updates
|
|
101
103
|
*
|
|
102
104
|
* @param filePath - Path to User Story markdown file
|
|
103
|
-
* @returns External ID reference if found, null otherwise
|
|
105
|
+
* @returns External ID reference with parent info if found, null otherwise
|
|
104
106
|
*/
|
|
105
107
|
async extractExternalIdFromFile(filePath) {
|
|
106
108
|
try {
|
|
@@ -114,15 +116,32 @@ export class DuplicateDetector {
|
|
|
114
116
|
filePath,
|
|
115
117
|
externalId: String(parsed.data.external_id),
|
|
116
118
|
platform: parsed.data.external_platform,
|
|
119
|
+
// Parent tracking from frontmatter
|
|
120
|
+
featureId: parsed.data.feature_id,
|
|
121
|
+
parentId: parsed.data.parent_id,
|
|
122
|
+
isOrphan: parsed.data.is_orphan,
|
|
123
|
+
adoWorkItemType: parsed.data.ado_work_item_type,
|
|
117
124
|
};
|
|
118
125
|
}
|
|
119
126
|
// Fallback: Parse external metadata section in content
|
|
120
127
|
const externalIdMatch = content.match(/^-\s+\*\*External ID\*\*:\s+(.+)$/m);
|
|
121
128
|
if (externalIdMatch) {
|
|
129
|
+
// Extract parent tracking info from content body
|
|
130
|
+
const featureIdMatch = content.match(/^-\s+\*\*Feature ID\*\*:\s+(.+)$/m);
|
|
131
|
+
const parentIdMatch = content.match(/^-\s+\*\*Parent ID\*\*:\s+(.+)$/m);
|
|
132
|
+
const orphanMatch = content.match(/^-\s+\*\*Orphan\*\*:\s+true/m);
|
|
133
|
+
const adoTypeMatch = content.match(/^-\s+\*\*ADO Work Item Type\*\*:\s+(.+)$/m);
|
|
134
|
+
const platformMatch = content.match(/^-\s+\*\*Platform\*\*:\s+(.+)$/m);
|
|
122
135
|
return {
|
|
123
136
|
usId: this.extractUsIdFromFile(filePath, content),
|
|
124
137
|
filePath,
|
|
125
138
|
externalId: externalIdMatch[1].trim(),
|
|
139
|
+
platform: platformMatch?.[1]?.trim()?.toLowerCase(),
|
|
140
|
+
// Parent tracking from content body
|
|
141
|
+
featureId: featureIdMatch?.[1]?.trim(),
|
|
142
|
+
parentId: parentIdMatch?.[1]?.trim(),
|
|
143
|
+
isOrphan: !!orphanMatch,
|
|
144
|
+
adoWorkItemType: adoTypeMatch?.[1]?.trim(),
|
|
126
145
|
};
|
|
127
146
|
}
|
|
128
147
|
return null;
|