specweave 0.28.67 → 0.29.0
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/helpers/issue-tracker/ado.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/ado.js +14 -5
- package/dist/src/cli/helpers/issue-tracker/ado.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 +231 -45
- 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 +1 -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-ado/lib/ado-project-detector.js +11 -5
- package/plugins/specweave-ado/lib/ado-project-detector.ts +16 -5
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +34 -0
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +51 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Sync Monitor Dashboard
|
|
2
|
+
|
|
3
|
+
Show sync orchestration status at a glance - jobs, notifications, and recent activity.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
/specweave:sync-monitor # Show dashboard
|
|
9
|
+
/specweave:sync-monitor --json # JSON output for scripting
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Arguments
|
|
13
|
+
|
|
14
|
+
- `--json`: Output machine-readable JSON instead of formatted dashboard
|
|
15
|
+
|
|
16
|
+
## Workflow
|
|
17
|
+
|
|
18
|
+
1. **Fetch dashboard data** from DashboardDataProvider:
|
|
19
|
+
- Get all scheduled job statuses (real-time)
|
|
20
|
+
- Get notification summary (5s cache)
|
|
21
|
+
- Get last 24h sync activity (1m cache)
|
|
22
|
+
|
|
23
|
+
2. **Display formatted dashboard**:
|
|
24
|
+
```
|
|
25
|
+
╔══════════════════════════════════════════════════════════════╗
|
|
26
|
+
║ SYNC MONITOR DASHBOARD ║
|
|
27
|
+
╠══════════════════════════════════════════════════════════════╣
|
|
28
|
+
║ SCHEDULED JOBS ║
|
|
29
|
+
╟──────────────────────────────────────────────────────────────╢
|
|
30
|
+
║ external-sync │ ✅ idle │ Last: 5m ago │ Next: 10m ║
|
|
31
|
+
║ discrepancy-check │ 🔄 running │ Started: 2m ago ║
|
|
32
|
+
║ living-docs-sync │ ⏸️ disabled ║
|
|
33
|
+
╠══════════════════════════════════════════════════════════════╣
|
|
34
|
+
║ PENDING NOTIFICATIONS (3) ║
|
|
35
|
+
╟──────────────────────────────────────────────────────────────╢
|
|
36
|
+
║ ❗ CRITICAL: GitHub sync failed (rate limited) ║
|
|
37
|
+
║ ⚠️ WARNING: 2 discrepancies detected in FS-045 ║
|
|
38
|
+
║ ℹ️ INFO: 107 items imported from JIRA (project CORE) ║
|
|
39
|
+
╠══════════════════════════════════════════════════════════════╣
|
|
40
|
+
║ RECENT SYNC ACTIVITY (last 24h) ║
|
|
41
|
+
╟──────────────────────────────────────────────────────────────╢
|
|
42
|
+
║ GitHub: 45 synced │ JIRA: 12 synced │ ADO: 0 synced ║
|
|
43
|
+
║ Success: 55 │ Failed: 2 │ Skipped (no permission): 8 ║
|
|
44
|
+
╚══════════════════════════════════════════════════════════════╝
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Output
|
|
48
|
+
|
|
49
|
+
- **Jobs Section**: Shows each scheduled job with status emoji, last run, next run
|
|
50
|
+
- **Notifications Section**: Shows pending notification count and recent items
|
|
51
|
+
- **Activity Section**: Shows last 24h sync stats by platform and result
|
|
52
|
+
|
|
53
|
+
## Related
|
|
54
|
+
|
|
55
|
+
- `/specweave:notifications`: Manage notifications
|
|
56
|
+
- `/specweave:discrepancies`: View and act on discrepancies
|
|
57
|
+
- `/specweave:sync-logs`: Query sync audit logs
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"hooks": [
|
|
6
6
|
{
|
|
7
7
|
"type": "command",
|
|
8
|
-
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/
|
|
8
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/universal/dispatcher.mjs\" session-start"
|
|
9
9
|
}
|
|
10
10
|
]
|
|
11
11
|
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"hooks": [
|
|
18
18
|
{
|
|
19
19
|
"type": "command",
|
|
20
|
-
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/
|
|
20
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/universal/dispatcher.mjs\" completion-guard"
|
|
21
21
|
}
|
|
22
22
|
]
|
|
23
23
|
}
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"hooks": [
|
|
30
30
|
{
|
|
31
31
|
"type": "command",
|
|
32
|
-
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/
|
|
32
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/universal/dispatcher.mjs\" post-tool-use"
|
|
33
33
|
}
|
|
34
34
|
]
|
|
35
35
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# scheduler-startup.sh - Check for due jobs on session start
|
|
3
|
+
# Called from session-start hook to initialize scheduler
|
|
4
|
+
set -e
|
|
5
|
+
|
|
6
|
+
# Skip if hooks disabled
|
|
7
|
+
[[ "${SPECWEAVE_DISABLE_HOOKS:-0}" == "1" ]] && exit 0
|
|
8
|
+
|
|
9
|
+
# Find project root
|
|
10
|
+
PROJECT_ROOT="$PWD"
|
|
11
|
+
while [[ "$PROJECT_ROOT" != "/" ]] && [[ ! -d "$PROJECT_ROOT/.specweave" ]]; do
|
|
12
|
+
PROJECT_ROOT=$(dirname "$PROJECT_ROOT")
|
|
13
|
+
done
|
|
14
|
+
|
|
15
|
+
# Exit if no .specweave directory
|
|
16
|
+
[[ ! -d "$PROJECT_ROOT/.specweave" ]] && exit 0
|
|
17
|
+
|
|
18
|
+
# Paths
|
|
19
|
+
SCHEDULED_JOBS_FILE="$PROJECT_ROOT/.specweave/state/scheduled-jobs.json"
|
|
20
|
+
CONFIG_FILE="$PROJECT_ROOT/.specweave/config.json"
|
|
21
|
+
|
|
22
|
+
# Check if scheduler is enabled
|
|
23
|
+
if [[ -f "$CONFIG_FILE" ]]; then
|
|
24
|
+
# Check if sync.orchestration.scheduler.enabled is true
|
|
25
|
+
# Use node for reliable JSON parsing
|
|
26
|
+
SCHEDULER_ENABLED=$(node -e "
|
|
27
|
+
try {
|
|
28
|
+
const config = require('$CONFIG_FILE');
|
|
29
|
+
const enabled = config?.sync?.orchestration?.scheduler?.enabled ?? false;
|
|
30
|
+
console.log(enabled ? 'true' : 'false');
|
|
31
|
+
} catch(e) {
|
|
32
|
+
console.log('false');
|
|
33
|
+
}
|
|
34
|
+
" 2>/dev/null || echo "false")
|
|
35
|
+
|
|
36
|
+
if [[ "$SCHEDULER_ENABLED" != "true" ]]; then
|
|
37
|
+
exit 0
|
|
38
|
+
fi
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# Check if scheduled jobs file exists
|
|
42
|
+
if [[ ! -f "$SCHEDULED_JOBS_FILE" ]]; then
|
|
43
|
+
exit 0
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# Use node to check for due jobs (robust JSON parsing)
|
|
47
|
+
DUE_JOBS=$(node -e "
|
|
48
|
+
const fs = require('fs');
|
|
49
|
+
try {
|
|
50
|
+
const data = JSON.parse(fs.readFileSync('$SCHEDULED_JOBS_FILE', 'utf-8'));
|
|
51
|
+
const now = Date.now();
|
|
52
|
+
const dueJobs = (data.jobs || []).filter(job => {
|
|
53
|
+
if (!job.schedule.enabled || job.status !== 'idle') return false;
|
|
54
|
+
if (!job.schedule.nextRun) return true;
|
|
55
|
+
return new Date(job.schedule.nextRun).getTime() <= now;
|
|
56
|
+
});
|
|
57
|
+
console.log(JSON.stringify(dueJobs.map(j => j.id)));
|
|
58
|
+
} catch(e) {
|
|
59
|
+
console.log('[]');
|
|
60
|
+
}
|
|
61
|
+
" 2>/dev/null || echo "[]")
|
|
62
|
+
|
|
63
|
+
# Exit if no due jobs
|
|
64
|
+
if [[ "$DUE_JOBS" == "[]" ]]; then
|
|
65
|
+
exit 0
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# Log due jobs (for debugging)
|
|
69
|
+
echo "📅 Due sync jobs: $DUE_JOBS" >> "$PROJECT_ROOT/.specweave/logs/scheduler.log" 2>/dev/null || true
|
|
70
|
+
|
|
71
|
+
# Output status (will be captured by hook system)
|
|
72
|
+
echo "{\"continue\": true, \"systemMessage\": \"📅 Scheduled sync jobs ready to run. Use /specweave:sync-now to execute.\"}"
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Universal Hook Dispatcher (Cross-Platform)
|
|
4
|
+
*
|
|
5
|
+
* Routes hook calls to the appropriate TypeScript implementation.
|
|
6
|
+
* Works on Windows, macOS, and Linux.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node dispatcher.mjs <hook-type>
|
|
10
|
+
*
|
|
11
|
+
* Where hook-type is one of:
|
|
12
|
+
* - session-start
|
|
13
|
+
* - post-tool-use
|
|
14
|
+
* - completion-guard
|
|
15
|
+
*
|
|
16
|
+
* @module hooks/universal/dispatcher
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { spawn } from 'child_process';
|
|
20
|
+
import { fileURLToPath } from 'url';
|
|
21
|
+
import { dirname, join, resolve } from 'path';
|
|
22
|
+
import { existsSync } from 'fs';
|
|
23
|
+
|
|
24
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
25
|
+
const __dirname = dirname(__filename);
|
|
26
|
+
|
|
27
|
+
// Hook type from arguments
|
|
28
|
+
const hookType = process.argv[2] || 'unknown';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Find the dist/hooks directory
|
|
32
|
+
*/
|
|
33
|
+
function findHooksDir() {
|
|
34
|
+
// Try multiple locations
|
|
35
|
+
const candidates = [
|
|
36
|
+
// Production: node_modules/specweave/dist/hooks
|
|
37
|
+
join(__dirname, '..', '..', '..', '..', 'node_modules', 'specweave', 'dist', 'hooks'),
|
|
38
|
+
// Development: project root dist/hooks
|
|
39
|
+
join(__dirname, '..', '..', '..', '..', 'dist', 'hooks'),
|
|
40
|
+
// Relative to this file
|
|
41
|
+
join(__dirname, '..', '..', '..', '..', 'src', 'hooks'),
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
for (const candidate of candidates) {
|
|
45
|
+
const resolved = resolve(candidate);
|
|
46
|
+
if (existsSync(resolved)) {
|
|
47
|
+
return resolved;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Run a hook script
|
|
56
|
+
*/
|
|
57
|
+
async function runHook(scriptName) {
|
|
58
|
+
const hooksDir = findHooksDir();
|
|
59
|
+
if (!hooksDir) {
|
|
60
|
+
// No hooks directory - just continue
|
|
61
|
+
console.log(JSON.stringify({ continue: true }));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const scriptPath = join(hooksDir, scriptName);
|
|
66
|
+
if (!existsSync(scriptPath) && !existsSync(scriptPath + '.js')) {
|
|
67
|
+
console.log(JSON.stringify({ continue: true }));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Spawn node to run the script
|
|
72
|
+
const child = spawn(process.execPath, [scriptPath], {
|
|
73
|
+
stdio: ['inherit', 'inherit', 'inherit'],
|
|
74
|
+
windowsHide: true,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
return new Promise((resolve) => {
|
|
78
|
+
child.on('exit', (code) => resolve(code || 0));
|
|
79
|
+
child.on('error', () => {
|
|
80
|
+
console.log(JSON.stringify({ continue: true }));
|
|
81
|
+
resolve(1);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Cached Git Bash path (computed once per session)
|
|
88
|
+
* undefined = not yet checked, null = checked and not found, string = path found
|
|
89
|
+
*/
|
|
90
|
+
let gitBashCache = undefined;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Find Git Bash on Windows (with caching)
|
|
94
|
+
*
|
|
95
|
+
* Checks common Git for Windows installation paths.
|
|
96
|
+
* Caches result to avoid repeated filesystem checks.
|
|
97
|
+
*/
|
|
98
|
+
function findGitBash() {
|
|
99
|
+
// Return cached result if available
|
|
100
|
+
if (gitBashCache !== undefined) {
|
|
101
|
+
return gitBashCache;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const gitBashPaths = [
|
|
105
|
+
// Environment variable paths with proper fallbacks
|
|
106
|
+
join(process.env.PROGRAMFILES || 'C:\\Program Files', 'Git', 'bin', 'bash.exe'),
|
|
107
|
+
join(process.env['PROGRAMFILES(X86)'] || 'C:\\Program Files (x86)', 'Git', 'bin', 'bash.exe'),
|
|
108
|
+
join(process.env.LOCALAPPDATA || '', 'Programs', 'Git', 'bin', 'bash.exe'),
|
|
109
|
+
// Hardcoded fallbacks for edge cases where env vars are missing
|
|
110
|
+
'C:\\Program Files\\Git\\bin\\bash.exe',
|
|
111
|
+
'C:\\Program Files (x86)\\Git\\bin\\bash.exe',
|
|
112
|
+
'C:\\Git\\bin\\bash.exe',
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
for (const p of gitBashPaths) {
|
|
116
|
+
if (p && existsSync(p)) {
|
|
117
|
+
gitBashCache = p;
|
|
118
|
+
return p;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
gitBashCache = null;
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Fallback to bash script if TypeScript not built
|
|
128
|
+
*
|
|
129
|
+
* @param bashScript - Name of the bash script (e.g., 'post-tool-use.sh')
|
|
130
|
+
* @param subdir - Subdirectory under v2 (e.g., 'dispatchers', 'guards')
|
|
131
|
+
*/
|
|
132
|
+
async function fallbackToBash(bashScript, subdir = 'dispatchers') {
|
|
133
|
+
const isWindows = process.platform === 'win32';
|
|
134
|
+
const bashDir = join(__dirname, '..', 'v2', subdir);
|
|
135
|
+
const scriptPath = join(bashDir, bashScript);
|
|
136
|
+
|
|
137
|
+
if (!existsSync(scriptPath)) {
|
|
138
|
+
console.log(JSON.stringify({ continue: true }));
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (isWindows) {
|
|
143
|
+
// Strategy 1: Git Bash (preferred - most common)
|
|
144
|
+
const bashExe = findGitBash();
|
|
145
|
+
if (bashExe) {
|
|
146
|
+
const child = spawn(bashExe, [scriptPath], {
|
|
147
|
+
stdio: ['inherit', 'inherit', 'inherit'],
|
|
148
|
+
windowsHide: true,
|
|
149
|
+
});
|
|
150
|
+
return new Promise((resolve) => {
|
|
151
|
+
child.on('exit', (code) => resolve(code || 0));
|
|
152
|
+
child.on('error', () => {
|
|
153
|
+
console.log(JSON.stringify({ continue: true }));
|
|
154
|
+
resolve(1);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Strategy 2: WSL (if Git Bash not available) - FALLBACK only
|
|
160
|
+
const wslPath = join(process.env.SYSTEMROOT || 'C:\\Windows', 'System32', 'wsl.exe');
|
|
161
|
+
if (existsSync(wslPath)) {
|
|
162
|
+
// Convert Windows path to WSL path:
|
|
163
|
+
// 1. Backslashes to forward slashes: C:\path\file -> C:/path/file
|
|
164
|
+
// 2. Drive letter to /mnt/: C:/path/file -> /mnt/c/path/file
|
|
165
|
+
// NOTE: Handle BOTH uppercase and lowercase drive letters (C: and c:)
|
|
166
|
+
const wslScriptPath = scriptPath
|
|
167
|
+
.replace(/\\/g, '/')
|
|
168
|
+
.replace(/^([A-Za-z]):/, (_, d) => `/mnt/${d.toLowerCase()}`);
|
|
169
|
+
const child = spawn('wsl', ['bash', wslScriptPath], {
|
|
170
|
+
stdio: ['inherit', 'inherit', 'inherit'],
|
|
171
|
+
windowsHide: true,
|
|
172
|
+
});
|
|
173
|
+
return new Promise((resolve) => {
|
|
174
|
+
child.on('exit', (code) => resolve(code || 0));
|
|
175
|
+
child.on('error', () => {
|
|
176
|
+
console.log(JSON.stringify({ continue: true }));
|
|
177
|
+
resolve(1);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Strategy 3: No bash available - output warning and continue
|
|
183
|
+
// Hooks should not block Claude Code operation
|
|
184
|
+
console.log(JSON.stringify({
|
|
185
|
+
continue: true,
|
|
186
|
+
systemMessage: 'SpecWeave hooks require Git Bash on Windows. Install from https://git-scm.com'
|
|
187
|
+
}));
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// POSIX (macOS, Linux) - run directly
|
|
192
|
+
const child = spawn('bash', [scriptPath], {
|
|
193
|
+
stdio: ['inherit', 'inherit', 'inherit'],
|
|
194
|
+
});
|
|
195
|
+
return new Promise((resolve) => {
|
|
196
|
+
child.on('exit', (code) => resolve(code || 0));
|
|
197
|
+
child.on('error', () => {
|
|
198
|
+
console.log(JSON.stringify({ continue: true }));
|
|
199
|
+
resolve(1);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Main routing
|
|
205
|
+
async function main() {
|
|
206
|
+
const isWindows = process.platform === 'win32';
|
|
207
|
+
|
|
208
|
+
try {
|
|
209
|
+
switch (hookType) {
|
|
210
|
+
case 'session-start':
|
|
211
|
+
// CRITICAL: On Windows, ALWAYS use TypeScript implementation
|
|
212
|
+
// Bash nohup/disown is unreliable in Git Bash (process may terminate early)
|
|
213
|
+
// TypeScript uses proper Windows detached spawn with windowsHide
|
|
214
|
+
if (isWindows) {
|
|
215
|
+
await runHook('session-start.js');
|
|
216
|
+
} else {
|
|
217
|
+
// POSIX: try TypeScript first (preferred), fallback to bash for compatibility
|
|
218
|
+
try {
|
|
219
|
+
await runHook('session-start.js');
|
|
220
|
+
} catch {
|
|
221
|
+
await fallbackToBash('session-start.sh', 'dispatchers');
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
break;
|
|
225
|
+
|
|
226
|
+
case 'post-tool-use':
|
|
227
|
+
// Currently only bash implementation
|
|
228
|
+
// On Windows without Git Bash, will gracefully skip
|
|
229
|
+
await fallbackToBash('post-tool-use.sh', 'dispatchers');
|
|
230
|
+
break;
|
|
231
|
+
|
|
232
|
+
case 'completion-guard':
|
|
233
|
+
// Guards are in a different subdirectory
|
|
234
|
+
// On Windows without Git Bash, will gracefully skip
|
|
235
|
+
await fallbackToBash('completion-guard.sh', 'guards');
|
|
236
|
+
break;
|
|
237
|
+
|
|
238
|
+
default:
|
|
239
|
+
console.log(JSON.stringify({ continue: true, error: `Unknown hook type: ${hookType}` }));
|
|
240
|
+
}
|
|
241
|
+
} catch (err) {
|
|
242
|
+
console.log(JSON.stringify({ continue: true, error: String(err) }));
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
main();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
:: Universal Session Start Hook for Windows
|
|
3
|
+
:: Calls the Node.js dispatcher
|
|
4
|
+
|
|
5
|
+
:: Find node.exe
|
|
6
|
+
where node >nul 2>&1
|
|
7
|
+
if %ERRORLEVEL% neq 0 (
|
|
8
|
+
echo {"continue": true, "error": "Node.js not found"}
|
|
9
|
+
exit /b 0
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
:: Get the directory of this script
|
|
13
|
+
set "SCRIPT_DIR=%~dp0"
|
|
14
|
+
|
|
15
|
+
:: Run the dispatcher
|
|
16
|
+
node "%SCRIPT_DIR%dispatcher.mjs" session-start
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Universal Session Start Hook for Windows PowerShell
|
|
2
|
+
# Calls the Node.js dispatcher for cross-platform compatibility
|
|
3
|
+
|
|
4
|
+
# Find node.exe
|
|
5
|
+
$nodePath = Get-Command node -ErrorAction SilentlyContinue
|
|
6
|
+
|
|
7
|
+
if (-not $nodePath) {
|
|
8
|
+
Write-Host '{"continue": true, "error": "Node.js not found"}'
|
|
9
|
+
exit 0
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
# Get script directory
|
|
13
|
+
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
14
|
+
|
|
15
|
+
# Run the dispatcher
|
|
16
|
+
& node "$scriptDir\dispatcher.mjs" session-start
|
|
@@ -15,10 +15,19 @@ done
|
|
|
15
15
|
# Consume stdin
|
|
16
16
|
cat > /dev/null
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
19
|
+
PROCESSOR="$HOOK_DIR/../queue/processor.sh"
|
|
20
|
+
SCHEDULER_STARTUP="$HOOK_DIR/../../lib/scheduler-startup.sh"
|
|
21
|
+
|
|
22
|
+
# Launch queue processor in background (daemon mode)
|
|
23
|
+
if [[ -f "$PROCESSOR" ]]; then
|
|
24
|
+
nohup bash "$PROCESSOR" --daemon > /dev/null 2>&1 &
|
|
25
|
+
disown 2>/dev/null
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
# Check for due scheduled jobs (non-blocking)
|
|
29
|
+
if [[ -f "$SCHEDULER_STARTUP" ]]; then
|
|
30
|
+
bash "$SCHEDULER_STARTUP" 2>/dev/null || true
|
|
31
|
+
fi
|
|
20
32
|
|
|
21
|
-
# Launch processor in background (daemon mode)
|
|
22
|
-
nohup bash "$PROCESSOR" --daemon > /dev/null 2>&1 &
|
|
23
|
-
disown 2>/dev/null
|
|
24
33
|
exit 0
|
|
@@ -247,6 +247,11 @@ export interface ExternalContainerContext {
|
|
|
247
247
|
}
|
|
248
248
|
/**
|
|
249
249
|
* Extended increment metadata with multi-project support (v0.29.0+)
|
|
250
|
+
*
|
|
251
|
+
* NOTE (v0.29.0): featureId field was REMOVED
|
|
252
|
+
* Feature ID is derived from increment number: 0081 → FS-081
|
|
253
|
+
* Use deriveFeatureId() from src/utils/feature-id-derivation.ts
|
|
254
|
+
* See ADR-0140 for rationale
|
|
250
255
|
*/
|
|
251
256
|
export interface IncrementMetadataV2 extends IncrementMetadata {
|
|
252
257
|
/** Single project ID (backward compatible) */
|
|
@@ -255,8 +260,6 @@ export interface IncrementMetadataV2 extends IncrementMetadata {
|
|
|
255
260
|
multiProject?: MultiProjectUserStory;
|
|
256
261
|
/** External container context for 2-level directory structure */
|
|
257
262
|
externalContainer?: ExternalContainerContext;
|
|
258
|
-
/** Feature ID this increment belongs to */
|
|
259
|
-
featureId?: string;
|
|
260
263
|
/** Epic ID if part of an epic */
|
|
261
264
|
epicId?: string;
|
|
262
265
|
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Discrepancy Viewer Skill
|
|
2
|
+
|
|
3
|
+
View and manage brownfield documentation discrepancies.
|
|
4
|
+
|
|
5
|
+
**Activates for**: discrepancies, documentation gaps, missing docs, stale docs, knowledge gaps, brownfield analysis results, DISC-0001, view discrepancy, list discrepancies
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
Brownfield discrepancies are documentation gaps detected during brownfield analysis:
|
|
10
|
+
|
|
11
|
+
| Type | Description |
|
|
12
|
+
|------|-------------|
|
|
13
|
+
| `missing-docs` | Code exists but has no documentation |
|
|
14
|
+
| `stale-docs` | Code changed but docs weren't updated |
|
|
15
|
+
| `knowledge-gap` | Module only one person has committed to |
|
|
16
|
+
| `orphan-doc` | Documentation for deleted code |
|
|
17
|
+
| `missing-adr` | Significant pattern without ADR |
|
|
18
|
+
|
|
19
|
+
## How to View Discrepancies
|
|
20
|
+
|
|
21
|
+
### List All Pending
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { BrownfieldDiscrepancyManager } from 'specweave/core/discrepancy';
|
|
25
|
+
|
|
26
|
+
const manager = new BrownfieldDiscrepancyManager(projectPath);
|
|
27
|
+
const discrepancies = await manager.listDiscrepancies();
|
|
28
|
+
|
|
29
|
+
// Display in table format
|
|
30
|
+
console.log('📋 BROWNFIELD DISCREPANCIES');
|
|
31
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
32
|
+
console.log('ID Type Priority Module Summary');
|
|
33
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
34
|
+
|
|
35
|
+
for (const disc of discrepancies) {
|
|
36
|
+
const priorityIcon = {
|
|
37
|
+
critical: '🔴',
|
|
38
|
+
high: '🟠',
|
|
39
|
+
medium: '🟡',
|
|
40
|
+
low: '🟢'
|
|
41
|
+
}[disc.priority];
|
|
42
|
+
|
|
43
|
+
console.log(`${disc.id} ${disc.type.padEnd(15)} ${priorityIcon} ${disc.priority.padEnd(8)} ${disc.module.padEnd(16)} ${disc.summary.slice(0, 30)}...`);
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Filter by Module
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
const discrepancies = await manager.listDiscrepancies({
|
|
51
|
+
module: 'payment-service'
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Filter by Type
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
const discrepancies = await manager.listDiscrepancies({
|
|
59
|
+
type: 'missing-docs'
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Filter by Priority
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
const discrepancies = await manager.listDiscrepancies({
|
|
67
|
+
priority: 'critical'
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### View Single Discrepancy
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
const disc = await manager.getDiscrepancy('DISC-0001');
|
|
75
|
+
|
|
76
|
+
console.log(`
|
|
77
|
+
🔍 DISCREPANCY DETAILS
|
|
78
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
79
|
+
|
|
80
|
+
ID: ${disc.id}
|
|
81
|
+
Type: ${disc.type}
|
|
82
|
+
Priority: ${disc.priority}
|
|
83
|
+
Module: ${disc.module}
|
|
84
|
+
Status: ${disc.status}
|
|
85
|
+
Confidence: ${disc.confidence}%
|
|
86
|
+
|
|
87
|
+
Summary: ${disc.summary}
|
|
88
|
+
Details: ${disc.details}
|
|
89
|
+
|
|
90
|
+
Code Location: ${disc.codeLocation || 'N/A'}
|
|
91
|
+
Doc Location: ${disc.docLocation || 'N/A'}
|
|
92
|
+
|
|
93
|
+
Detected: ${disc.detectedAt}
|
|
94
|
+
Last Check: ${disc.lastChecked}
|
|
95
|
+
`);
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Ignore a Discrepancy
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
await manager.ignoreDiscrepancy('DISC-0001', 'False positive - test code');
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Output Format
|
|
105
|
+
|
|
106
|
+
### Table Format (Default)
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
📋 BROWNFIELD DISCREPANCIES (15 pending)
|
|
110
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
111
|
+
|
|
112
|
+
ID Type Priority Module Summary
|
|
113
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
114
|
+
DISC-0001 missing-docs 🔴 critical payment-service 12 undocumented exports
|
|
115
|
+
DISC-0002 stale-docs 🟠 high auth Login flow docs outdated
|
|
116
|
+
DISC-0003 knowledge-gap 🟡 medium legacy-adapter Single contributor module
|
|
117
|
+
DISC-0004 missing-adr 🟢 low cache No ADR for caching strategy
|
|
118
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
119
|
+
|
|
120
|
+
Use '/specweave:discrepancies show <ID>' for details
|
|
121
|
+
Use '/specweave:discrepancy-to-increment <ID> <ID>...' to create an increment
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Statistics
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
📊 DISCREPANCY STATS
|
|
128
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
129
|
+
|
|
130
|
+
Total: 47
|
|
131
|
+
Pending: 15
|
|
132
|
+
In Progress: 5
|
|
133
|
+
Resolved: 25
|
|
134
|
+
Ignored: 2
|
|
135
|
+
|
|
136
|
+
By Type:
|
|
137
|
+
missing-docs: 22 (47%)
|
|
138
|
+
stale-docs: 8 (17%)
|
|
139
|
+
knowledge-gap: 7 (15%)
|
|
140
|
+
orphan-doc: 5 (11%)
|
|
141
|
+
missing-adr: 5 (11%)
|
|
142
|
+
|
|
143
|
+
By Priority:
|
|
144
|
+
Critical: 3
|
|
145
|
+
High: 8
|
|
146
|
+
Medium: 12
|
|
147
|
+
Low: 24
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Related
|
|
151
|
+
|
|
152
|
+
- `/specweave:discrepancy-to-increment` - Convert discrepancies to increments
|
|
153
|
+
- `/specweave:jobs` - Monitor brownfield analysis jobs
|
|
154
|
+
- `brownfield-analyzer` skill - Run new analysis
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as fs from "../../../src/utils/fs-native.js";
|
|
2
2
|
import * as path from "path";
|
|
3
|
+
import { normalizeToProjectId } from "../../../src/utils/project-id-generator.js";
|
|
3
4
|
const PROJECT_KEYWORDS = {
|
|
4
5
|
"AuthService": [
|
|
5
6
|
"authentication",
|
|
@@ -414,7 +415,8 @@ async function createProjectFolders(baseDir, strategy, projects) {
|
|
|
414
415
|
switch (strategy) {
|
|
415
416
|
case "project-per-team":
|
|
416
417
|
for (const project2 of projects) {
|
|
417
|
-
const
|
|
418
|
+
const normalizedName = normalizeToProjectId(project2);
|
|
419
|
+
const projectPath = path.join(specsPath, normalizedName);
|
|
418
420
|
await fs.ensureDir(projectPath);
|
|
419
421
|
await createProjectReadme(projectPath, project2);
|
|
420
422
|
}
|
|
@@ -423,10 +425,12 @@ async function createProjectFolders(baseDir, strategy, projects) {
|
|
|
423
425
|
const areaPaths = process.env.AZURE_DEVOPS_AREA_PATHS?.split(",").map((a) => a.trim()) || [];
|
|
424
426
|
const project = projects[0];
|
|
425
427
|
if (project) {
|
|
426
|
-
const
|
|
428
|
+
const normalizedProject = normalizeToProjectId(project);
|
|
429
|
+
const projectPath = path.join(specsPath, normalizedProject);
|
|
427
430
|
await fs.ensureDir(projectPath);
|
|
428
431
|
for (const area of areaPaths) {
|
|
429
|
-
const
|
|
432
|
+
const normalizedArea = normalizeToProjectId(area);
|
|
433
|
+
const areaPath = path.join(projectPath, normalizedArea);
|
|
430
434
|
await fs.ensureDir(areaPath);
|
|
431
435
|
}
|
|
432
436
|
}
|
|
@@ -435,10 +439,12 @@ async function createProjectFolders(baseDir, strategy, projects) {
|
|
|
435
439
|
const teams = process.env.AZURE_DEVOPS_TEAMS?.split(",").map((t) => t.trim()) || [];
|
|
436
440
|
const proj = projects[0];
|
|
437
441
|
if (proj) {
|
|
438
|
-
const
|
|
442
|
+
const normalizedProj = normalizeToProjectId(proj);
|
|
443
|
+
const projectPath = path.join(specsPath, normalizedProj);
|
|
439
444
|
await fs.ensureDir(projectPath);
|
|
440
445
|
for (const team of teams) {
|
|
441
|
-
const
|
|
446
|
+
const normalizedTeam = normalizeToProjectId(team);
|
|
447
|
+
const teamPath = path.join(projectPath, normalizedTeam);
|
|
442
448
|
await fs.ensureDir(teamPath);
|
|
443
449
|
}
|
|
444
450
|
}
|