specweave 0.30.19 β 0.32.2
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 +176 -2
- package/README.md +22 -0
- package/bin/specweave.js +18 -1
- package/dist/src/cli/commands/cache.d.ts +17 -0
- package/dist/src/cli/commands/cache.d.ts.map +1 -0
- package/dist/src/cli/commands/cache.js +126 -0
- package/dist/src/cli/commands/cache.js.map +1 -0
- package/dist/src/cli/commands/init.js +1 -1
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/plan/increment-detector.js +2 -2
- package/dist/src/cli/commands/plan/increment-detector.js.map +1 -1
- package/dist/src/cli/commands/sync-spec-commits.js +1 -1
- package/dist/src/cli/commands/sync-spec-commits.js.map +1 -1
- package/dist/src/cli/commands/sync-specs.js +2 -2
- package/dist/src/cli/commands/sync-specs.js.map +1 -1
- package/dist/src/cli/helpers/github/increment-profile-selector.js +1 -1
- package/dist/src/cli/helpers/github/increment-profile-selector.js.map +1 -1
- package/dist/src/cli/workers/living-docs-worker.js +66 -1
- package/dist/src/cli/workers/living-docs-worker.js.map +1 -1
- package/dist/src/config/types.d.ts +203 -1208
- package/dist/src/config/types.d.ts.map +1 -1
- package/dist/src/core/discrepancy/increment-generator.d.ts.map +1 -1
- package/dist/src/core/discrepancy/increment-generator.js +5 -2
- package/dist/src/core/discrepancy/increment-generator.js.map +1 -1
- package/dist/src/core/external-tools/external-items-counter.d.ts +62 -0
- package/dist/src/core/external-tools/external-items-counter.d.ts.map +1 -0
- package/dist/src/core/external-tools/external-items-counter.js +206 -0
- package/dist/src/core/external-tools/external-items-counter.js.map +1 -0
- package/dist/src/core/external-tools/external-items-display.d.ts +39 -0
- package/dist/src/core/external-tools/external-items-display.d.ts.map +1 -0
- package/dist/src/core/external-tools/external-items-display.js +185 -0
- package/dist/src/core/external-tools/external-items-display.js.map +1 -0
- package/dist/src/core/external-tools/index.d.ts +8 -0
- package/dist/src/core/external-tools/index.d.ts.map +1 -0
- package/dist/src/core/external-tools/index.js +8 -0
- package/dist/src/core/external-tools/index.js.map +1 -0
- package/dist/src/core/external-tools/providers/ado-items-adapter.d.ts +39 -0
- package/dist/src/core/external-tools/providers/ado-items-adapter.d.ts.map +1 -0
- package/dist/src/core/external-tools/providers/ado-items-adapter.js +188 -0
- package/dist/src/core/external-tools/providers/ado-items-adapter.js.map +1 -0
- package/dist/src/core/external-tools/providers/github-items-adapter.d.ts +38 -0
- package/dist/src/core/external-tools/providers/github-items-adapter.d.ts.map +1 -0
- package/dist/src/core/external-tools/providers/github-items-adapter.js +136 -0
- package/dist/src/core/external-tools/providers/github-items-adapter.js.map +1 -0
- package/dist/src/core/external-tools/providers/index.d.ts +7 -0
- package/dist/src/core/external-tools/providers/index.d.ts.map +1 -0
- package/dist/src/core/external-tools/providers/index.js +7 -0
- package/dist/src/core/external-tools/providers/index.js.map +1 -0
- package/dist/src/core/external-tools/providers/jira-items-adapter.d.ts +42 -0
- package/dist/src/core/external-tools/providers/jira-items-adapter.d.ts.map +1 -0
- package/dist/src/core/external-tools/providers/jira-items-adapter.js +153 -0
- package/dist/src/core/external-tools/providers/jira-items-adapter.js.map +1 -0
- package/dist/src/core/external-tools/types.d.ts +78 -0
- package/dist/src/core/external-tools/types.d.ts.map +1 -0
- package/dist/src/core/external-tools/types.js +19 -0
- package/dist/src/core/external-tools/types.js.map +1 -0
- package/dist/src/core/increment/duplicate-detector.js +2 -2
- package/dist/src/core/increment/duplicate-detector.js.map +1 -1
- package/dist/src/core/increment/increment-archiver.d.ts +24 -0
- package/dist/src/core/increment/increment-archiver.d.ts.map +1 -1
- package/dist/src/core/increment/increment-archiver.js +59 -2
- package/dist/src/core/increment/increment-archiver.js.map +1 -1
- package/dist/src/core/increment/increment-status.js +2 -2
- package/dist/src/core/increment/increment-status.js.map +1 -1
- package/dist/src/core/increment/increment-utils.d.ts +98 -37
- package/dist/src/core/increment/increment-utils.d.ts.map +1 -1
- package/dist/src/core/increment/increment-utils.js +119 -68
- package/dist/src/core/increment/increment-utils.js.map +1 -1
- package/dist/src/core/increment/metadata-validator.js +1 -1
- package/dist/src/core/increment/metadata-validator.js.map +1 -1
- package/dist/src/core/increment/status-change-sync-trigger.d.ts.map +1 -1
- package/dist/src/core/increment/status-change-sync-trigger.js +4 -0
- package/dist/src/core/increment/status-change-sync-trigger.js.map +1 -1
- package/dist/src/core/living-docs/feature-id-manager.js +1 -1
- package/dist/src/core/living-docs/feature-id-manager.js.map +1 -1
- package/dist/src/core/living-docs/hierarchy-mapper.js +3 -3
- package/dist/src/core/living-docs/hierarchy-mapper.js.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.d.ts +18 -0
- package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js +247 -0
- package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.d.ts +15 -0
- package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.js +138 -0
- package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.d.ts +24 -0
- package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.js +198 -0
- package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.d.ts +17 -0
- package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.js +241 -0
- package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/index.d.ts +28 -0
- package/dist/src/core/living-docs/intelligent-analyzer/index.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/index.js +197 -0
- package/dist/src/core/living-docs/intelligent-analyzer/index.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.d.ts +18 -0
- package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js +154 -0
- package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.d.ts +42 -0
- package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.js +343 -0
- package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts +146 -0
- package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/types.js +7 -0
- package/dist/src/core/living-docs/intelligent-analyzer/types.js.map +1 -0
- package/dist/src/core/living-docs/living-docs-sync.d.ts +5 -0
- package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.js +36 -2
- package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
- package/dist/src/core/llm/providers/azure-openai-provider.d.ts.map +1 -1
- package/dist/src/core/llm/providers/azure-openai-provider.js +1 -0
- package/dist/src/core/llm/providers/azure-openai-provider.js.map +1 -1
- package/dist/src/core/llm/providers/bedrock-provider.d.ts.map +1 -1
- package/dist/src/core/llm/providers/bedrock-provider.js +2 -0
- package/dist/src/core/llm/providers/bedrock-provider.js.map +1 -1
- package/dist/src/core/llm/providers/openai-provider.d.ts.map +1 -1
- package/dist/src/core/llm/providers/openai-provider.js +1 -0
- package/dist/src/core/llm/providers/openai-provider.js.map +1 -1
- package/dist/src/core/llm/providers/vertex-ai-provider.d.ts.map +1 -1
- package/dist/src/core/llm/providers/vertex-ai-provider.js +1 -0
- package/dist/src/core/llm/providers/vertex-ai-provider.js.map +1 -1
- package/dist/src/core/sync/spec-increment-mapper.js +3 -3
- package/dist/src/core/sync/spec-increment-mapper.js.map +1 -1
- package/dist/src/importers/item-converter.d.ts +25 -0
- package/dist/src/importers/item-converter.d.ts.map +1 -1
- package/dist/src/importers/item-converter.js +135 -5
- package/dist/src/importers/item-converter.js.map +1 -1
- package/dist/src/init/architecture/types.d.ts +33 -140
- package/dist/src/init/architecture/types.d.ts.map +1 -1
- package/dist/src/init/compliance/types.d.ts +30 -27
- package/dist/src/init/compliance/types.d.ts.map +1 -1
- package/dist/src/init/repo/types.d.ts +11 -34
- package/dist/src/init/repo/types.d.ts.map +1 -1
- package/dist/src/init/research/src/config/types.d.ts +15 -82
- package/dist/src/init/research/src/config/types.d.ts.map +1 -1
- package/dist/src/init/research/types.d.ts +38 -93
- package/dist/src/init/research/types.d.ts.map +1 -1
- package/dist/src/init/team/types.d.ts +4 -42
- package/dist/src/init/team/types.d.ts.map +1 -1
- package/dist/src/types/dashboard-cache.d.ts +181 -0
- package/dist/src/types/dashboard-cache.d.ts.map +1 -0
- package/dist/src/types/dashboard-cache.js +65 -0
- package/dist/src/types/dashboard-cache.js.map +1 -0
- package/dist/src/utils/docs-validator.d.ts +131 -0
- package/dist/src/utils/docs-validator.d.ts.map +1 -0
- package/dist/src/utils/docs-validator.js +529 -0
- package/dist/src/utils/docs-validator.js.map +1 -0
- package/dist/src/utils/feature-id-collision.js +1 -1
- package/dist/src/utils/feature-id-collision.js.map +1 -1
- package/dist/src/utils/html-to-mdx.d.ts +1 -0
- package/dist/src/utils/html-to-mdx.d.ts.map +1 -1
- package/dist/src/utils/html-to-mdx.js +43 -5
- package/dist/src/utils/html-to-mdx.js.map +1 -1
- package/package.json +1 -5
- package/plugins/specweave/agents/pm/AGENT.md +10 -7
- package/plugins/specweave/commands/specweave-archive-features.md +5 -7
- package/plugins/specweave/commands/specweave-archive.md +2 -1
- package/plugins/specweave/commands/specweave-do.md +35 -1
- package/plugins/specweave/commands/specweave-done.md +96 -0
- package/plugins/specweave/commands/specweave-external.md +150 -0
- package/plugins/specweave/commands/specweave-import-external.md +45 -18
- package/plugins/specweave/commands/specweave-increment.md +331 -33
- package/plugins/specweave/commands/specweave-jobs.md +2 -2
- package/plugins/specweave/commands/specweave-progress.md +4 -4
- package/plugins/specweave/commands/specweave-restore-feature.md +5 -4
- package/plugins/specweave/commands/specweave-sync-docs.md +1 -1
- package/plugins/specweave/commands/specweave-sync-specs.md +216 -322
- package/plugins/specweave/commands/specweave-validate-features.md +13 -8
- package/plugins/specweave/hooks/docs-changed.sh.backup +79 -0
- package/plugins/specweave/hooks/hooks.json +33 -4
- package/plugins/specweave/hooks/human-input-required.sh.backup +75 -0
- package/plugins/specweave/hooks/lib/common-setup.sh +375 -0
- package/plugins/specweave/hooks/lib/crash-prevention.sh +336 -0
- package/plugins/specweave/hooks/post-first-increment.sh.backup +61 -0
- package/plugins/specweave/hooks/post-increment-change.sh.backup +98 -0
- package/plugins/specweave/hooks/post-increment-completion.sh.backup +231 -0
- package/plugins/specweave/hooks/post-increment-planning.sh.backup +1048 -0
- package/plugins/specweave/hooks/post-increment-status-change.sh.backup +147 -0
- package/plugins/specweave/hooks/post-spec-update.sh.backup +158 -0
- package/plugins/specweave/hooks/post-task-completion.sh +4 -23
- package/plugins/specweave/hooks/post-user-story-complete.sh.backup +179 -0
- package/plugins/specweave/hooks/pre-command-deduplication.sh +1 -6
- package/plugins/specweave/hooks/pre-command-deduplication.sh.backup +83 -0
- package/plugins/specweave/hooks/pre-implementation.sh.backup +67 -0
- package/plugins/specweave/hooks/pre-task-completion.sh +8 -37
- package/plugins/specweave/hooks/pre-task-completion.sh.backup +194 -0
- package/plugins/specweave/hooks/pre-tool-use.sh +2 -11
- package/plugins/specweave/hooks/pre-tool-use.sh.backup +133 -0
- package/plugins/specweave/hooks/universal/dispatcher.mjs +135 -42
- package/plugins/specweave/hooks/universal/fail-fast-wrapper.sh +183 -0
- package/plugins/specweave/hooks/universal/hook-wrapper.cmd +26 -26
- package/plugins/specweave/hooks/universal/session-start.cmd +16 -16
- package/plugins/specweave/hooks/universal/session-start.ps1 +16 -16
- package/plugins/specweave/hooks/user-prompt-submit.sh +140 -38
- package/plugins/specweave/hooks/user-prompt-submit.sh.backup +386 -0
- package/plugins/specweave/hooks/v2/dispatchers/post-tool-use.sh +12 -0
- package/plugins/specweave/hooks/v2/dispatchers/session-start.sh +89 -0
- package/plugins/specweave/hooks/v2/guards/bash-file-guard.sh +211 -0
- package/plugins/specweave/hooks/v2/guards/bash-file-guard.test.sh +163 -0
- package/plugins/specweave/hooks/v2/guards/completion-guard.sh +26 -28
- package/plugins/specweave/hooks/v2/guards/features-folder-guard.sh +50 -0
- package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.js +2 -2
- package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.js.map +1 -1
- package/plugins/specweave/scripts/README.md +166 -0
- package/plugins/specweave/scripts/cleanup-state.sh +142 -0
- package/plugins/specweave/scripts/force-kill.sh +142 -0
- package/plugins/specweave/scripts/jobs.js +171 -0
- package/plugins/specweave/scripts/progress.js +170 -0
- package/plugins/specweave/scripts/read-costs.sh +132 -0
- package/plugins/specweave/scripts/read-jobs.sh +324 -0
- package/plugins/specweave/scripts/read-progress.sh +185 -0
- package/plugins/specweave/scripts/read-status.sh +146 -0
- package/plugins/specweave/scripts/read-workflow.sh +173 -0
- package/plugins/specweave/scripts/rebuild-dashboard-cache.sh +327 -0
- package/plugins/specweave/scripts/session-watchdog.sh +192 -0
- package/plugins/specweave/scripts/status.js +154 -0
- package/plugins/specweave/scripts/update-dashboard-cache.sh +281 -0
- package/plugins/specweave/skills/increment-planner/SKILL.md +333 -24
- package/plugins/specweave/skills/increment-planner/templates/spec-multi-project.md +17 -9
- package/plugins/specweave/skills/increment-planner/templates/spec-single-project.md +6 -2
- package/plugins/specweave/skills/instant-status/SKILL.md +70 -0
- package/plugins/specweave-ado/hooks/post-living-docs-update.sh.backup +353 -0
- package/plugins/specweave-ado/hooks/post-task-completion.sh.backup +172 -0
- package/plugins/specweave-ado/lib/enhanced-ado-sync.js +170 -0
- package/plugins/specweave-docs/commands/build.md +32 -4
- package/plugins/specweave-docs/commands/preview.md +43 -1
- package/plugins/specweave-docs/commands/validate.md +250 -0
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +1262 -626
- package/plugins/specweave-github/hooks/post-task-completion.sh.backup +258 -0
- package/plugins/specweave-github/lib/enhanced-github-sync.js +220 -0
- package/plugins/specweave-jira/hooks/post-task-completion.sh.backup +172 -0
- package/plugins/specweave-jira/lib/enhanced-jira-sync.js +134 -0
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +1254 -939
- package/plugins/specweave-release/hooks/post-task-completion.sh.backup +110 -0
- package/plugins/specweave/hooks/post-edit-spec.sh +0 -265
- package/plugins/specweave/hooks/post-write-spec.sh +0 -267
- package/plugins/specweave/hooks/pre-edit-spec.sh +0 -151
- package/plugins/specweave/hooks/pre-write-spec.sh +0 -151
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# rebuild-dashboard-cache.sh - Full rebuild of dashboard cache
|
|
3
|
+
#
|
|
4
|
+
# Usage: bash rebuild-dashboard-cache.sh [--quiet]
|
|
5
|
+
#
|
|
6
|
+
# Scans all increments in .specweave/increments/ and builds complete cache.
|
|
7
|
+
# Uses atomic write pattern (temp file + rename) to prevent corruption.
|
|
8
|
+
#
|
|
9
|
+
# Output: .specweave/state/dashboard.json
|
|
10
|
+
#
|
|
11
|
+
# Compatible with bash 3.x (macOS default)
|
|
12
|
+
|
|
13
|
+
set -e
|
|
14
|
+
|
|
15
|
+
QUIET="${1:-}"
|
|
16
|
+
log() {
|
|
17
|
+
[[ "$QUIET" == "--quiet" ]] || echo "$@"
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
# Find project root
|
|
21
|
+
PROJECT_ROOT="$PWD"
|
|
22
|
+
while [[ "$PROJECT_ROOT" != "/" ]] && [[ ! -d "$PROJECT_ROOT/.specweave" ]]; do
|
|
23
|
+
PROJECT_ROOT=$(dirname "$PROJECT_ROOT")
|
|
24
|
+
done
|
|
25
|
+
|
|
26
|
+
if [[ ! -d "$PROJECT_ROOT/.specweave" ]]; then
|
|
27
|
+
echo "β No .specweave directory found"
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
INCREMENTS_DIR="$PROJECT_ROOT/.specweave/increments"
|
|
32
|
+
STATE_DIR="$PROJECT_ROOT/.specweave/state"
|
|
33
|
+
CACHE_FILE="$STATE_DIR/dashboard.json"
|
|
34
|
+
TEMP_FILE="$STATE_DIR/.dashboard.json.tmp.$$"
|
|
35
|
+
ARCHIVE_DIR="$INCREMENTS_DIR/_archive"
|
|
36
|
+
|
|
37
|
+
# Ensure state directory exists
|
|
38
|
+
mkdir -p "$STATE_DIR"
|
|
39
|
+
|
|
40
|
+
# Check if jq is available
|
|
41
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
42
|
+
echo "β jq is required for cache operations. Install with: brew install jq"
|
|
43
|
+
exit 1
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
log "π Rebuilding dashboard cache..."
|
|
47
|
+
|
|
48
|
+
# Initialize counters using simple variables (bash 3.x compatible)
|
|
49
|
+
status_backlog=0
|
|
50
|
+
status_planned=0
|
|
51
|
+
status_active=0
|
|
52
|
+
status_paused=0
|
|
53
|
+
status_ready_for_review=0
|
|
54
|
+
status_completed=0
|
|
55
|
+
status_abandoned=0
|
|
56
|
+
|
|
57
|
+
type_feature=0
|
|
58
|
+
type_hotfix=0
|
|
59
|
+
type_bug=0
|
|
60
|
+
type_refactor=0
|
|
61
|
+
type_experiment=0
|
|
62
|
+
type_change_request=0
|
|
63
|
+
|
|
64
|
+
priority_P0=0
|
|
65
|
+
priority_P1=0
|
|
66
|
+
priority_P2=0
|
|
67
|
+
priority_P3=0
|
|
68
|
+
|
|
69
|
+
archived_count=0
|
|
70
|
+
total_count=0
|
|
71
|
+
|
|
72
|
+
# Start building JSON
|
|
73
|
+
increments_json="{}"
|
|
74
|
+
mtimes_json="{}"
|
|
75
|
+
|
|
76
|
+
# Process each increment directory
|
|
77
|
+
for increment_dir in "$INCREMENTS_DIR"/[0-9]*/; do
|
|
78
|
+
[[ -d "$increment_dir" ]] || continue
|
|
79
|
+
|
|
80
|
+
increment_id=$(basename "$increment_dir")
|
|
81
|
+
metadata_file="$increment_dir/metadata.json"
|
|
82
|
+
tasks_file="$increment_dir/tasks.md"
|
|
83
|
+
spec_file="$increment_dir/spec.md"
|
|
84
|
+
|
|
85
|
+
# Skip if no metadata
|
|
86
|
+
[[ -f "$metadata_file" ]] || continue
|
|
87
|
+
|
|
88
|
+
total_count=$((total_count + 1))
|
|
89
|
+
|
|
90
|
+
# Read metadata
|
|
91
|
+
status=$(jq -r '.status // "backlog"' "$metadata_file" 2>/dev/null)
|
|
92
|
+
type=$(jq -r '.type // "feature"' "$metadata_file" 2>/dev/null)
|
|
93
|
+
priority=$(jq -r '.priority // "P1"' "$metadata_file" 2>/dev/null)
|
|
94
|
+
title=$(jq -r '.title // ""' "$metadata_file" 2>/dev/null)
|
|
95
|
+
project=$(jq -r '.project // ""' "$metadata_file" 2>/dev/null)
|
|
96
|
+
created_at=$(jq -r '.createdAt // ""' "$metadata_file" 2>/dev/null)
|
|
97
|
+
|
|
98
|
+
# Read user stories
|
|
99
|
+
user_stories=$(jq -c '.userStories // []' "$metadata_file" 2>/dev/null || echo "[]")
|
|
100
|
+
|
|
101
|
+
# Count tasks
|
|
102
|
+
total_tasks=0
|
|
103
|
+
completed_tasks=0
|
|
104
|
+
if [[ -f "$tasks_file" ]]; then
|
|
105
|
+
# Count ### T- headers for total
|
|
106
|
+
total_tasks=$(grep -c "^### T-" "$tasks_file" 2>/dev/null) || total_tasks=0
|
|
107
|
+
# Count [x] for completed
|
|
108
|
+
completed_tasks=$(grep "^\*\*Status\*\*:.*\[x\]" "$tasks_file" 2>/dev/null | wc -l | tr -d ' ') || completed_tasks=0
|
|
109
|
+
# Ensure numeric
|
|
110
|
+
total_tasks="${total_tasks:-0}"
|
|
111
|
+
completed_tasks="${completed_tasks:-0}"
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
# Fallback to metadata task counts if available
|
|
115
|
+
if [[ "$total_tasks" -eq 0 ]]; then
|
|
116
|
+
total_tasks=$(jq -r '.tasks.total // 0' "$metadata_file" 2>/dev/null) || total_tasks=0
|
|
117
|
+
completed_tasks=$(jq -r '.tasks.completed // 0' "$metadata_file" 2>/dev/null) || completed_tasks=0
|
|
118
|
+
total_tasks="${total_tasks:-0}"
|
|
119
|
+
completed_tasks="${completed_tasks:-0}"
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
# Count ACs from spec.md
|
|
123
|
+
total_acs=0
|
|
124
|
+
completed_acs=0
|
|
125
|
+
if [[ -f "$spec_file" ]]; then
|
|
126
|
+
# Count all AC checkboxes
|
|
127
|
+
total_acs=$(grep -c "\- \[.\] \*\*AC-" "$spec_file" 2>/dev/null) || total_acs=0
|
|
128
|
+
completed_acs=$(grep -c "\- \[x\] \*\*AC-" "$spec_file" 2>/dev/null) || completed_acs=0
|
|
129
|
+
total_acs="${total_acs:-0}"
|
|
130
|
+
completed_acs="${completed_acs:-0}"
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
# Fallback to metadata AC counts
|
|
134
|
+
if [[ "$total_acs" -eq 0 ]]; then
|
|
135
|
+
total_acs=$(jq -r '.acceptanceCriteria.total // 0' "$metadata_file" 2>/dev/null) || total_acs=0
|
|
136
|
+
completed_acs=$(jq -r '.acceptanceCriteria.satisfied // 0' "$metadata_file" 2>/dev/null) || completed_acs=0
|
|
137
|
+
total_acs="${total_acs:-0}"
|
|
138
|
+
completed_acs="${completed_acs:-0}"
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
# Get last activity from file mtime
|
|
142
|
+
last_activity=""
|
|
143
|
+
if [[ "$(uname)" == "Darwin" ]]; then
|
|
144
|
+
last_activity=$(stat -f "%Sm" -t "%Y-%m-%dT%H:%M:%SZ" "$metadata_file" 2>/dev/null || echo "")
|
|
145
|
+
else
|
|
146
|
+
last_activity=$(stat -c "%y" "$metadata_file" 2>/dev/null | sed 's/ /T/' | cut -d. -f1)Z || echo ""
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
# Build increment JSON
|
|
150
|
+
increment_json=$(jq -n \
|
|
151
|
+
--arg status "$status" \
|
|
152
|
+
--arg type "$type" \
|
|
153
|
+
--arg priority "$priority" \
|
|
154
|
+
--arg title "$title" \
|
|
155
|
+
--arg project "$project" \
|
|
156
|
+
--argjson total_tasks "$total_tasks" \
|
|
157
|
+
--argjson completed_tasks "$completed_tasks" \
|
|
158
|
+
--argjson total_acs "$total_acs" \
|
|
159
|
+
--argjson completed_acs "$completed_acs" \
|
|
160
|
+
--arg created_at "$created_at" \
|
|
161
|
+
--arg last_activity "$last_activity" \
|
|
162
|
+
--argjson user_stories "$user_stories" \
|
|
163
|
+
'{
|
|
164
|
+
status: $status,
|
|
165
|
+
type: $type,
|
|
166
|
+
priority: $priority,
|
|
167
|
+
title: $title,
|
|
168
|
+
project: $project,
|
|
169
|
+
tasks: { total: $total_tasks, completed: $completed_tasks },
|
|
170
|
+
acs: { total: $total_acs, completed: $completed_acs },
|
|
171
|
+
createdAt: $created_at,
|
|
172
|
+
lastActivity: $last_activity,
|
|
173
|
+
userStories: $user_stories
|
|
174
|
+
}')
|
|
175
|
+
|
|
176
|
+
# Add to increments object
|
|
177
|
+
increments_json=$(echo "$increments_json" | jq --arg id "$increment_id" --argjson inc "$increment_json" '.[$id] = $inc')
|
|
178
|
+
|
|
179
|
+
# Collect mtimes for stale detection
|
|
180
|
+
meta_mtime=0
|
|
181
|
+
tasks_mtime=0
|
|
182
|
+
spec_mtime=0
|
|
183
|
+
if [[ "$(uname)" == "Darwin" ]]; then
|
|
184
|
+
[[ -f "$metadata_file" ]] && meta_mtime=$(stat -f "%m" "$metadata_file" 2>/dev/null || echo "0")
|
|
185
|
+
[[ -f "$tasks_file" ]] && tasks_mtime=$(stat -f "%m" "$tasks_file" 2>/dev/null || echo "0")
|
|
186
|
+
[[ -f "$spec_file" ]] && spec_mtime=$(stat -f "%m" "$spec_file" 2>/dev/null || echo "0")
|
|
187
|
+
else
|
|
188
|
+
[[ -f "$metadata_file" ]] && meta_mtime=$(stat -c "%Y" "$metadata_file" 2>/dev/null || echo "0")
|
|
189
|
+
[[ -f "$tasks_file" ]] && tasks_mtime=$(stat -c "%Y" "$tasks_file" 2>/dev/null || echo "0")
|
|
190
|
+
[[ -f "$spec_file" ]] && spec_mtime=$(stat -c "%Y" "$spec_file" 2>/dev/null || echo "0")
|
|
191
|
+
fi
|
|
192
|
+
|
|
193
|
+
mtimes_json=$(echo "$mtimes_json" | jq --arg id "$increment_id" \
|
|
194
|
+
--argjson meta "$meta_mtime" \
|
|
195
|
+
--argjson tasks "$tasks_mtime" \
|
|
196
|
+
--argjson spec "$spec_mtime" \
|
|
197
|
+
'.[$id] = { metadata: $meta, tasks: $tasks, spec: $spec }')
|
|
198
|
+
|
|
199
|
+
# Update status counters (bash 3.x compatible)
|
|
200
|
+
case "$status" in
|
|
201
|
+
backlog) status_backlog=$((status_backlog + 1)) ;;
|
|
202
|
+
planned) status_planned=$((status_planned + 1)) ;;
|
|
203
|
+
active) status_active=$((status_active + 1)) ;;
|
|
204
|
+
paused) status_paused=$((status_paused + 1)) ;;
|
|
205
|
+
ready_for_review) status_ready_for_review=$((status_ready_for_review + 1)) ;;
|
|
206
|
+
completed) status_completed=$((status_completed + 1)) ;;
|
|
207
|
+
abandoned) status_abandoned=$((status_abandoned + 1)) ;;
|
|
208
|
+
*) status_backlog=$((status_backlog + 1)) ;;
|
|
209
|
+
esac
|
|
210
|
+
|
|
211
|
+
# Update type counters
|
|
212
|
+
case "$type" in
|
|
213
|
+
feature) type_feature=$((type_feature + 1)) ;;
|
|
214
|
+
hotfix) type_hotfix=$((type_hotfix + 1)) ;;
|
|
215
|
+
bug) type_bug=$((type_bug + 1)) ;;
|
|
216
|
+
refactor) type_refactor=$((type_refactor + 1)) ;;
|
|
217
|
+
experiment) type_experiment=$((type_experiment + 1)) ;;
|
|
218
|
+
change-request) type_change_request=$((type_change_request + 1)) ;;
|
|
219
|
+
*) type_feature=$((type_feature + 1)) ;;
|
|
220
|
+
esac
|
|
221
|
+
|
|
222
|
+
# Update priority counters
|
|
223
|
+
case "$priority" in
|
|
224
|
+
P0) priority_P0=$((priority_P0 + 1)) ;;
|
|
225
|
+
P1) priority_P1=$((priority_P1 + 1)) ;;
|
|
226
|
+
P2) priority_P2=$((priority_P2 + 1)) ;;
|
|
227
|
+
P3) priority_P3=$((priority_P3 + 1)) ;;
|
|
228
|
+
*) priority_P1=$((priority_P1 + 1)) ;;
|
|
229
|
+
esac
|
|
230
|
+
done
|
|
231
|
+
|
|
232
|
+
# Count archived increments
|
|
233
|
+
if [[ -d "$ARCHIVE_DIR" ]]; then
|
|
234
|
+
archived_count=$(find "$ARCHIVE_DIR" -maxdepth 1 -type d -name "[0-9]*" 2>/dev/null | wc -l | tr -d ' ')
|
|
235
|
+
fi
|
|
236
|
+
|
|
237
|
+
# Build jobs section (read from existing jobs file if present)
|
|
238
|
+
jobs_json='{"running":[],"paused":[],"failed":[],"completedCount":0}'
|
|
239
|
+
JOBS_FILE="$STATE_DIR/background-jobs.json"
|
|
240
|
+
if [[ -f "$JOBS_FILE" ]]; then
|
|
241
|
+
running=$(jq -c '[.[] | select(.status == "running")]' "$JOBS_FILE" 2>/dev/null || echo "[]")
|
|
242
|
+
paused=$(jq -c '[.[] | select(.status == "paused")]' "$JOBS_FILE" 2>/dev/null || echo "[]")
|
|
243
|
+
failed=$(jq -c '[.[] | select(.status == "failed")]' "$JOBS_FILE" 2>/dev/null || echo "[]")
|
|
244
|
+
completed_count=$(jq '[.[] | select(.status == "completed")] | length' "$JOBS_FILE" 2>/dev/null || echo "0")
|
|
245
|
+
jobs_json=$(jq -n \
|
|
246
|
+
--argjson running "$running" \
|
|
247
|
+
--argjson paused "$paused" \
|
|
248
|
+
--argjson failed "$failed" \
|
|
249
|
+
--argjson completed_count "$completed_count" \
|
|
250
|
+
'{running: $running, paused: $paused, failed: $failed, completedCount: $completed_count}')
|
|
251
|
+
fi
|
|
252
|
+
|
|
253
|
+
# Build costs section (placeholder - populated by cost tracking hooks)
|
|
254
|
+
costs_json='{"totalTokens":0,"totalCost":0,"totalSavings":0,"byIncrement":{}}'
|
|
255
|
+
COSTS_FILE="$STATE_DIR/costs.json"
|
|
256
|
+
if [[ -f "$COSTS_FILE" ]]; then
|
|
257
|
+
costs_json=$(cat "$COSTS_FILE" 2>/dev/null || echo "$costs_json")
|
|
258
|
+
fi
|
|
259
|
+
|
|
260
|
+
# Build complete cache
|
|
261
|
+
cache_json=$(jq -n \
|
|
262
|
+
--argjson version 1 \
|
|
263
|
+
--arg updated_at "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
|
|
264
|
+
--argjson increments "$increments_json" \
|
|
265
|
+
--argjson total "$total_count" \
|
|
266
|
+
--argjson active "$status_active" \
|
|
267
|
+
--argjson paused "$status_paused" \
|
|
268
|
+
--argjson backlog "$status_backlog" \
|
|
269
|
+
--argjson planned "$status_planned" \
|
|
270
|
+
--argjson ready_for_review "$status_ready_for_review" \
|
|
271
|
+
--argjson completed "$status_completed" \
|
|
272
|
+
--argjson abandoned "$status_abandoned" \
|
|
273
|
+
--argjson archived "$archived_count" \
|
|
274
|
+
--argjson feature "$type_feature" \
|
|
275
|
+
--argjson hotfix "$type_hotfix" \
|
|
276
|
+
--argjson bug "$type_bug" \
|
|
277
|
+
--argjson refactor "$type_refactor" \
|
|
278
|
+
--argjson experiment "$type_experiment" \
|
|
279
|
+
--argjson change_request "$type_change_request" \
|
|
280
|
+
--argjson p0 "$priority_P0" \
|
|
281
|
+
--argjson p1 "$priority_P1" \
|
|
282
|
+
--argjson p2 "$priority_P2" \
|
|
283
|
+
--argjson p3 "$priority_P3" \
|
|
284
|
+
--argjson jobs "$jobs_json" \
|
|
285
|
+
--argjson costs "$costs_json" \
|
|
286
|
+
--argjson mtimes "$mtimes_json" \
|
|
287
|
+
'{
|
|
288
|
+
version: $version,
|
|
289
|
+
updatedAt: $updated_at,
|
|
290
|
+
increments: $increments,
|
|
291
|
+
summary: {
|
|
292
|
+
total: $total,
|
|
293
|
+
active: $active,
|
|
294
|
+
paused: $paused,
|
|
295
|
+
backlog: $backlog,
|
|
296
|
+
planned: $planned,
|
|
297
|
+
ready_for_review: $ready_for_review,
|
|
298
|
+
completed: $completed,
|
|
299
|
+
abandoned: $abandoned,
|
|
300
|
+
archived: $archived,
|
|
301
|
+
byType: {
|
|
302
|
+
feature: $feature,
|
|
303
|
+
hotfix: $hotfix,
|
|
304
|
+
bug: $bug,
|
|
305
|
+
refactor: $refactor,
|
|
306
|
+
experiment: $experiment,
|
|
307
|
+
"change-request": $change_request
|
|
308
|
+
},
|
|
309
|
+
byPriority: {
|
|
310
|
+
P0: $p0,
|
|
311
|
+
P1: $p1,
|
|
312
|
+
P2: $p2,
|
|
313
|
+
P3: $p3
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
jobs: $jobs,
|
|
317
|
+
costs: $costs,
|
|
318
|
+
mtimes: $mtimes
|
|
319
|
+
}')
|
|
320
|
+
|
|
321
|
+
# Atomic write: write to temp file, then rename
|
|
322
|
+
echo "$cache_json" > "$TEMP_FILE"
|
|
323
|
+
mv "$TEMP_FILE" "$CACHE_FILE"
|
|
324
|
+
|
|
325
|
+
log "β
Dashboard cache rebuilt"
|
|
326
|
+
log " π $total_count increments | $status_active active | $status_completed completed"
|
|
327
|
+
log " π Cache: $CACHE_FILE"
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# SpecWeave Session Watchdog
|
|
3
|
+
# Monitors Claude Code sessions and alerts when stuck
|
|
4
|
+
# Usage: bash session-watchdog.sh [--daemon] [--interval=60]
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
# Configuration
|
|
9
|
+
STUCK_THRESHOLD_SECONDS="${STUCK_THRESHOLD:-300}" # 5 minutes
|
|
10
|
+
CHECK_INTERVAL="${CHECK_INTERVAL:-60}" # 1 minute
|
|
11
|
+
SPECWEAVE_ROOT="${SPECWEAVE_ROOT:-.specweave}"
|
|
12
|
+
SIGNAL_FILE="${SPECWEAVE_ROOT}/state/.session-stuck"
|
|
13
|
+
HEARTBEAT_FILE="${SPECWEAVE_ROOT}/state/.heartbeat"
|
|
14
|
+
DAEMON_MODE=false
|
|
15
|
+
|
|
16
|
+
# Colors
|
|
17
|
+
RED='\033[0;31m'
|
|
18
|
+
YELLOW='\033[1;33m'
|
|
19
|
+
GREEN='\033[0;32m'
|
|
20
|
+
NC='\033[0m'
|
|
21
|
+
|
|
22
|
+
# Parse arguments
|
|
23
|
+
for arg in "$@"; do
|
|
24
|
+
case $arg in
|
|
25
|
+
--daemon)
|
|
26
|
+
DAEMON_MODE=true
|
|
27
|
+
;;
|
|
28
|
+
--interval=*)
|
|
29
|
+
CHECK_INTERVAL="${arg#*=}"
|
|
30
|
+
;;
|
|
31
|
+
--threshold=*)
|
|
32
|
+
STUCK_THRESHOLD_SECONDS="${arg#*=}"
|
|
33
|
+
;;
|
|
34
|
+
esac
|
|
35
|
+
done
|
|
36
|
+
|
|
37
|
+
log() {
|
|
38
|
+
echo -e "[$(date '+%H:%M:%S')] $1"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
send_notification() {
|
|
42
|
+
local title="$1"
|
|
43
|
+
local message="$2"
|
|
44
|
+
|
|
45
|
+
# macOS notification
|
|
46
|
+
if command -v osascript &> /dev/null; then
|
|
47
|
+
osascript -e "display notification \"$message\" with title \"$title\" sound name \"Basso\""
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# Linux notification (if available)
|
|
51
|
+
if command -v notify-send &> /dev/null; then
|
|
52
|
+
notify-send "$title" "$message" --urgency=critical
|
|
53
|
+
fi
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
get_file_age_seconds() {
|
|
57
|
+
local file="$1"
|
|
58
|
+
if [[ ! -f "$file" ]]; then
|
|
59
|
+
echo "999999"
|
|
60
|
+
return
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
local now
|
|
64
|
+
local mtime
|
|
65
|
+
now=$(date +%s)
|
|
66
|
+
|
|
67
|
+
if [[ "$(uname)" == "Darwin" ]]; then
|
|
68
|
+
mtime=$(stat -f %m "$file")
|
|
69
|
+
else
|
|
70
|
+
mtime=$(stat -c %Y "$file")
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
echo $((now - mtime))
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
check_lock_file() {
|
|
77
|
+
local lock_file="${SPECWEAVE_ROOT}/state/.processor.lock"
|
|
78
|
+
if [[ -f "$lock_file" ]]; then
|
|
79
|
+
local age
|
|
80
|
+
age=$(get_file_age_seconds "$lock_file")
|
|
81
|
+
if [[ "$age" -gt "$STUCK_THRESHOLD_SECONDS" ]]; then
|
|
82
|
+
log "${RED}β οΈ STUCK DETECTED: Lock file held for ${age}s (threshold: ${STUCK_THRESHOLD_SECONDS}s)${NC}"
|
|
83
|
+
return 1
|
|
84
|
+
fi
|
|
85
|
+
fi
|
|
86
|
+
return 0
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
check_heartbeat() {
|
|
90
|
+
if [[ -f "$HEARTBEAT_FILE" ]]; then
|
|
91
|
+
local age
|
|
92
|
+
age=$(get_file_age_seconds "$HEARTBEAT_FILE")
|
|
93
|
+
if [[ "$age" -gt "$STUCK_THRESHOLD_SECONDS" ]]; then
|
|
94
|
+
log "${RED}β οΈ STUCK DETECTED: No heartbeat for ${age}s${NC}"
|
|
95
|
+
return 1
|
|
96
|
+
fi
|
|
97
|
+
fi
|
|
98
|
+
return 0
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
check_mcp_drops() {
|
|
102
|
+
local debug_log="$HOME/.claude/debug/latest"
|
|
103
|
+
if [[ -f "$debug_log" ]]; then
|
|
104
|
+
local drops
|
|
105
|
+
drops=$(grep -c "WS-IDE connection dropped" "$debug_log" 2>/dev/null | head -1 || echo "0")
|
|
106
|
+
drops="${drops//[^0-9]/}"
|
|
107
|
+
drops="${drops:-0}"
|
|
108
|
+
if [[ "$drops" -gt 3 ]]; then
|
|
109
|
+
log "${YELLOW}β οΈ MCP instability: $drops connection drops detected${NC}"
|
|
110
|
+
return 1
|
|
111
|
+
fi
|
|
112
|
+
fi
|
|
113
|
+
return 0
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
check_zombie_processes() {
|
|
117
|
+
local zombies
|
|
118
|
+
zombies=$(pgrep -f "cat.*EOF" 2>/dev/null | wc -l | tr -d ' ')
|
|
119
|
+
if [[ "$zombies" -gt 0 ]]; then
|
|
120
|
+
log "${RED}β οΈ STUCK DETECTED: $zombies zombie heredoc processes${NC}"
|
|
121
|
+
return 1
|
|
122
|
+
fi
|
|
123
|
+
return 0
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
check_session_health() {
|
|
127
|
+
local stuck=false
|
|
128
|
+
local reasons=()
|
|
129
|
+
|
|
130
|
+
if ! check_lock_file; then
|
|
131
|
+
stuck=true
|
|
132
|
+
reasons+=("Lock file stale")
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
if ! check_heartbeat; then
|
|
136
|
+
stuck=true
|
|
137
|
+
reasons+=("No heartbeat")
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
if ! check_mcp_drops; then
|
|
141
|
+
reasons+=("MCP unstable")
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
if ! check_zombie_processes; then
|
|
145
|
+
stuck=true
|
|
146
|
+
reasons+=("Zombie processes")
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
if [[ "$stuck" == "true" ]]; then
|
|
150
|
+
local reason_str
|
|
151
|
+
reason_str=$(IFS=", "; echo "${reasons[*]}")
|
|
152
|
+
|
|
153
|
+
# Create signal file
|
|
154
|
+
echo "stuck_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$SIGNAL_FILE"
|
|
155
|
+
echo "reasons=$reason_str" >> "$SIGNAL_FILE"
|
|
156
|
+
|
|
157
|
+
send_notification "π¨ Claude Code Stuck" "$reason_str - Run cleanup-state.sh"
|
|
158
|
+
|
|
159
|
+
log "${RED}ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ${NC}"
|
|
160
|
+
log "${RED}SESSION STUCK DETECTED${NC}"
|
|
161
|
+
log "${RED}Reasons: $reason_str${NC}"
|
|
162
|
+
log ""
|
|
163
|
+
log "Recovery steps:"
|
|
164
|
+
log " 1. Press Ctrl+C multiple times in Claude Code terminal"
|
|
165
|
+
log " 2. Run: bash plugins/specweave/scripts/cleanup-state.sh"
|
|
166
|
+
log " 3. If VS Code, restart Extension Host (Cmd+Shift+P)"
|
|
167
|
+
log " 4. Restart Claude Code"
|
|
168
|
+
log "${RED}ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ${NC}"
|
|
169
|
+
|
|
170
|
+
return 1
|
|
171
|
+
else
|
|
172
|
+
# Remove signal file if exists
|
|
173
|
+
rm -f "$SIGNAL_FILE"
|
|
174
|
+
log "${GREEN}β Session healthy${NC}"
|
|
175
|
+
return 0
|
|
176
|
+
fi
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
# Main execution
|
|
180
|
+
if [[ "$DAEMON_MODE" == "true" ]]; then
|
|
181
|
+
log "Starting session watchdog daemon (interval: ${CHECK_INTERVAL}s, threshold: ${STUCK_THRESHOLD_SECONDS}s)"
|
|
182
|
+
log "Press Ctrl+C to stop"
|
|
183
|
+
|
|
184
|
+
while true; do
|
|
185
|
+
check_session_health || true
|
|
186
|
+
sleep "$CHECK_INTERVAL"
|
|
187
|
+
done
|
|
188
|
+
else
|
|
189
|
+
log "Running single health check..."
|
|
190
|
+
check_session_health
|
|
191
|
+
exit $?
|
|
192
|
+
fi
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Instant Increment Status Overview
|
|
4
|
+
*
|
|
5
|
+
* Executed by UserPromptSubmit hook for /specweave:status
|
|
6
|
+
* Bypasses LLM entirely - output shown directly to user
|
|
7
|
+
*
|
|
8
|
+
* Usage: node status.js [--help]
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import fs from 'fs';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
|
|
14
|
+
// Handle --help
|
|
15
|
+
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
16
|
+
console.log(`
|
|
17
|
+
SpecWeave Instant Status
|
|
18
|
+
|
|
19
|
+
USAGE
|
|
20
|
+
node plugins/specweave/scripts/status.js [options]
|
|
21
|
+
specweave status [options]
|
|
22
|
+
|
|
23
|
+
OPTIONS
|
|
24
|
+
--help, -h Show this help message
|
|
25
|
+
|
|
26
|
+
DESCRIPTION
|
|
27
|
+
Shows increment status overview grouped by status (active, paused, completed, etc.).
|
|
28
|
+
This script bypasses LLM processing for instant results (<100ms).
|
|
29
|
+
|
|
30
|
+
EXECUTION PATHS
|
|
31
|
+
1. Claude Code: /specweave:status (hook intercepts, <100ms)
|
|
32
|
+
2. Any LLM: Skill instructs to run this script (~2s)
|
|
33
|
+
3. Terminal: specweave status (~500ms)
|
|
34
|
+
|
|
35
|
+
EXAMPLES
|
|
36
|
+
node plugins/specweave/scripts/status.js
|
|
37
|
+
specweave status
|
|
38
|
+
specweave status --verbose
|
|
39
|
+
`);
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const cwd = process.cwd();
|
|
44
|
+
const incrementsDir = path.join(cwd, '.specweave/increments');
|
|
45
|
+
const archiveDir = path.join(incrementsDir, '_archive');
|
|
46
|
+
|
|
47
|
+
// Check if increments directory exists
|
|
48
|
+
if (!fs.existsSync(incrementsDir)) {
|
|
49
|
+
console.log('No SpecWeave project found (missing .specweave/increments/)');
|
|
50
|
+
process.exit(0);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Find increments
|
|
54
|
+
const entries = fs.readdirSync(incrementsDir, { withFileTypes: true });
|
|
55
|
+
const incrementFolders = entries
|
|
56
|
+
.filter(e => e.isDirectory() && !e.name.startsWith('_') && /^\d{4}/.test(e.name))
|
|
57
|
+
.map(e => e.name);
|
|
58
|
+
|
|
59
|
+
// Count archived
|
|
60
|
+
let archivedCount = 0;
|
|
61
|
+
if (fs.existsSync(archiveDir)) {
|
|
62
|
+
const archived = fs.readdirSync(archiveDir, { withFileTypes: true });
|
|
63
|
+
archivedCount = archived.filter(e => e.isDirectory() && /^\d{4}/.test(e.name)).length;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Parse increment status
|
|
67
|
+
function getStatus(folder) {
|
|
68
|
+
const metaPath = path.join(incrementsDir, folder, 'metadata.json');
|
|
69
|
+
if (fs.existsSync(metaPath)) {
|
|
70
|
+
try {
|
|
71
|
+
const meta = JSON.parse(fs.readFileSync(metaPath, 'utf-8'));
|
|
72
|
+
return meta.status || 'unknown';
|
|
73
|
+
} catch {}
|
|
74
|
+
}
|
|
75
|
+
return 'unknown';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Group by status
|
|
79
|
+
const byStatus = {};
|
|
80
|
+
for (const folder of incrementFolders) {
|
|
81
|
+
const status = getStatus(folder);
|
|
82
|
+
if (!byStatus[status]) byStatus[status] = [];
|
|
83
|
+
byStatus[status].push(folder);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Display
|
|
87
|
+
console.log('\nπ SpecWeave Status Overview\n');
|
|
88
|
+
|
|
89
|
+
const statusOrder = ['active', 'planning', 'in-progress', 'ready_for_review', 'paused', 'backlog', 'completed', 'abandoned'];
|
|
90
|
+
const statusIcons = {
|
|
91
|
+
'active': 'π',
|
|
92
|
+
'planning': 'π',
|
|
93
|
+
'in-progress': 'βοΈ',
|
|
94
|
+
'ready_for_review': 'π',
|
|
95
|
+
'paused': 'βΈοΈ',
|
|
96
|
+
'backlog': 'π',
|
|
97
|
+
'completed': 'β
',
|
|
98
|
+
'abandoned': 'β'
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
let hasOutput = false;
|
|
102
|
+
|
|
103
|
+
for (const status of statusOrder) {
|
|
104
|
+
const items = byStatus[status];
|
|
105
|
+
if (items && items.length > 0) {
|
|
106
|
+
hasOutput = true;
|
|
107
|
+
const icon = statusIcons[status] || 'β’';
|
|
108
|
+
console.log(`${icon} ${status.replace('_', ' ')} (${items.length}):`);
|
|
109
|
+
|
|
110
|
+
// Show first 5, summarize rest
|
|
111
|
+
const toShow = items.slice(0, 5);
|
|
112
|
+
for (const item of toShow) {
|
|
113
|
+
console.log(` ${item}`);
|
|
114
|
+
}
|
|
115
|
+
if (items.length > 5) {
|
|
116
|
+
console.log(` ... and ${items.length - 5} more`);
|
|
117
|
+
}
|
|
118
|
+
console.log('');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Handle unknown statuses
|
|
123
|
+
for (const status of Object.keys(byStatus)) {
|
|
124
|
+
if (!statusOrder.includes(status)) {
|
|
125
|
+
const items = byStatus[status];
|
|
126
|
+
if (items.length > 0) {
|
|
127
|
+
hasOutput = true;
|
|
128
|
+
console.log(`? ${status} (${items.length}):`);
|
|
129
|
+
for (const item of items.slice(0, 3)) {
|
|
130
|
+
console.log(` ${item}`);
|
|
131
|
+
}
|
|
132
|
+
console.log('');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (!hasOutput) {
|
|
138
|
+
console.log('No increments found.');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Summary line
|
|
142
|
+
const total = incrementFolders.length;
|
|
143
|
+
const activeCount = (byStatus['active']?.length || 0) +
|
|
144
|
+
(byStatus['planning']?.length || 0) +
|
|
145
|
+
(byStatus['in-progress']?.length || 0);
|
|
146
|
+
const completedCount = byStatus['completed']?.length || 0;
|
|
147
|
+
|
|
148
|
+
console.log('β'.repeat(40));
|
|
149
|
+
console.log(`Total: ${total} increment(s) | Active: ${activeCount} | Completed: ${completedCount} | Archived: ${archivedCount}`);
|
|
150
|
+
console.log('');
|
|
151
|
+
console.log('π‘ Commands:');
|
|
152
|
+
console.log(' /specweave:progress Show task progress');
|
|
153
|
+
console.log(' /specweave:do Execute current tasks');
|
|
154
|
+
console.log(' /specweave:done <id> Close increment');
|