@leeovery/claude-technical-workflows 2.0.29 → 2.0.30
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leeovery/claude-technical-workflows",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.30",
|
|
4
4
|
"description": "Technical workflow skills & commands for Claude Code",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Lee Overy <me@leeovery.com>",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"skills",
|
|
21
21
|
"commands",
|
|
22
22
|
"agents",
|
|
23
|
-
"hooks"
|
|
23
|
+
"hooks",
|
|
24
|
+
"scripts"
|
|
24
25
|
]
|
|
25
26
|
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# discover-spec-state.sh
|
|
4
|
+
#
|
|
5
|
+
# Discovers the current state of discussions, specifications, and cache
|
|
6
|
+
# for the workflow:start-specification command.
|
|
7
|
+
#
|
|
8
|
+
# Outputs structured YAML that the command can consume directly.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
set -eo pipefail
|
|
12
|
+
|
|
13
|
+
DISCUSSION_DIR="docs/workflow/discussion"
|
|
14
|
+
SPEC_DIR="docs/workflow/specification"
|
|
15
|
+
CACHE_FILE="docs/workflow/.cache/discussion-consolidation-analysis.md"
|
|
16
|
+
|
|
17
|
+
# Helper: Extract a frontmatter field value from a file
|
|
18
|
+
# Usage: extract_field <file> <field_name>
|
|
19
|
+
extract_field() {
|
|
20
|
+
local file="$1"
|
|
21
|
+
local field="$2"
|
|
22
|
+
# Read until --- (end of frontmatter), find field, extract value
|
|
23
|
+
# Use grep -m1 and || true to handle no match gracefully
|
|
24
|
+
sed -n '/^---$/,/^---$/p' "$file" 2>/dev/null | \
|
|
25
|
+
grep -m1 "^${field}:" | \
|
|
26
|
+
sed "s/^${field}:[[:space:]]*//" || true
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
# Helper: Extract array field from frontmatter (returns space-separated values)
|
|
30
|
+
# Usage: extract_array_field <file> <field_name>
|
|
31
|
+
extract_array_field() {
|
|
32
|
+
local file="$1"
|
|
33
|
+
local field="$2"
|
|
34
|
+
local result
|
|
35
|
+
# Look for field followed by array items (- item), excluding --- delimiters
|
|
36
|
+
result=$(sed -n '/^---$/,/^---$/p' "$file" 2>/dev/null | \
|
|
37
|
+
grep -v "^---$" | \
|
|
38
|
+
sed -n "/^${field}:/,/^[a-z_]*:/p" | \
|
|
39
|
+
grep "^[[:space:]]*-" | \
|
|
40
|
+
sed 's/^[[:space:]]*-[[:space:]]*//' | \
|
|
41
|
+
tr '\n' ' ' | \
|
|
42
|
+
sed 's/[[:space:]]*$//' || true)
|
|
43
|
+
echo "$result"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# Start YAML output
|
|
47
|
+
echo "# Specification Command State Discovery"
|
|
48
|
+
echo "# Generated: $(date -Iseconds)"
|
|
49
|
+
echo ""
|
|
50
|
+
|
|
51
|
+
#
|
|
52
|
+
# DISCUSSIONS
|
|
53
|
+
#
|
|
54
|
+
echo "discussions:"
|
|
55
|
+
|
|
56
|
+
if [ -d "$DISCUSSION_DIR" ] && [ -n "$(ls -A "$DISCUSSION_DIR" 2>/dev/null)" ]; then
|
|
57
|
+
for file in "$DISCUSSION_DIR"/*.md; do
|
|
58
|
+
[ -f "$file" ] || continue
|
|
59
|
+
|
|
60
|
+
name=$(basename "$file" .md)
|
|
61
|
+
status=$(extract_field "$file" "status")
|
|
62
|
+
status=${status:-"unknown"}
|
|
63
|
+
|
|
64
|
+
# Check if this discussion has a corresponding individual spec
|
|
65
|
+
has_individual_spec="false"
|
|
66
|
+
if [ -f "$SPEC_DIR/${name}.md" ]; then
|
|
67
|
+
has_individual_spec="true"
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
echo " - name: \"$name\""
|
|
71
|
+
echo " status: \"$status\""
|
|
72
|
+
echo " has_individual_spec: $has_individual_spec"
|
|
73
|
+
done
|
|
74
|
+
else
|
|
75
|
+
echo " [] # No discussions found"
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
echo ""
|
|
79
|
+
|
|
80
|
+
#
|
|
81
|
+
# SPECIFICATIONS
|
|
82
|
+
#
|
|
83
|
+
echo "specifications:"
|
|
84
|
+
|
|
85
|
+
if [ -d "$SPEC_DIR" ] && [ -n "$(ls -A "$SPEC_DIR" 2>/dev/null)" ]; then
|
|
86
|
+
for file in "$SPEC_DIR"/*.md; do
|
|
87
|
+
[ -f "$file" ] || continue
|
|
88
|
+
|
|
89
|
+
name=$(basename "$file" .md)
|
|
90
|
+
status=$(extract_field "$file" "status")
|
|
91
|
+
status=${status:-"active"}
|
|
92
|
+
|
|
93
|
+
superseded_by=$(extract_field "$file" "superseded_by")
|
|
94
|
+
sources=$(extract_array_field "$file" "sources")
|
|
95
|
+
|
|
96
|
+
echo " - name: \"$name\""
|
|
97
|
+
echo " status: \"$status\""
|
|
98
|
+
|
|
99
|
+
if [ -n "$superseded_by" ]; then
|
|
100
|
+
echo " superseded_by: \"$superseded_by\""
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
if [ -n "$sources" ]; then
|
|
104
|
+
echo " sources:"
|
|
105
|
+
for src in $sources; do
|
|
106
|
+
echo " - \"$src\""
|
|
107
|
+
done
|
|
108
|
+
fi
|
|
109
|
+
done
|
|
110
|
+
else
|
|
111
|
+
echo " [] # No specifications found"
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
echo ""
|
|
115
|
+
|
|
116
|
+
#
|
|
117
|
+
# CACHE STATE
|
|
118
|
+
#
|
|
119
|
+
echo "cache:"
|
|
120
|
+
|
|
121
|
+
if [ -f "$CACHE_FILE" ]; then
|
|
122
|
+
echo " exists: true"
|
|
123
|
+
|
|
124
|
+
cached_checksum=$(extract_field "$CACHE_FILE" "checksum")
|
|
125
|
+
cached_date=$(extract_field "$CACHE_FILE" "generated")
|
|
126
|
+
|
|
127
|
+
echo " cached_checksum: \"${cached_checksum:-unknown}\""
|
|
128
|
+
echo " cached_date: \"${cached_date:-unknown}\""
|
|
129
|
+
|
|
130
|
+
# Extract anchored names (groupings that have existing specs)
|
|
131
|
+
# These are the grouping names from the cache that have corresponding specs
|
|
132
|
+
echo " anchored_names:"
|
|
133
|
+
|
|
134
|
+
# Parse the cache file for grouping names (### Name format)
|
|
135
|
+
# and check if a spec exists for each
|
|
136
|
+
anchored_found=false
|
|
137
|
+
while IFS= read -r grouping_name; do
|
|
138
|
+
# Clean the name (remove any trailing annotations, lowercase, spaces to hyphens)
|
|
139
|
+
clean_name=$(echo "$grouping_name" | sed 's/[[:space:]]*(.*)//' | tr '[:upper:]' '[:lower:]' | tr ' ' '-')
|
|
140
|
+
if [ -f "$SPEC_DIR/${clean_name}.md" ]; then
|
|
141
|
+
echo " - \"$clean_name\""
|
|
142
|
+
anchored_found=true
|
|
143
|
+
fi
|
|
144
|
+
done < <(grep "^### " "$CACHE_FILE" 2>/dev/null | sed 's/^### //' || true)
|
|
145
|
+
|
|
146
|
+
if [ "$anchored_found" = false ]; then
|
|
147
|
+
echo " [] # No anchored names found"
|
|
148
|
+
fi
|
|
149
|
+
else
|
|
150
|
+
echo " exists: false"
|
|
151
|
+
echo " cached_checksum: null"
|
|
152
|
+
echo " cached_date: null"
|
|
153
|
+
echo " anchored_names: []"
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
echo ""
|
|
157
|
+
|
|
158
|
+
#
|
|
159
|
+
# CURRENT CHECKSUM
|
|
160
|
+
#
|
|
161
|
+
echo "current_state:"
|
|
162
|
+
|
|
163
|
+
if [ -d "$DISCUSSION_DIR" ] && [ -n "$(ls -A "$DISCUSSION_DIR" 2>/dev/null)" ]; then
|
|
164
|
+
# Compute checksum of all discussion files (deterministic via sorted glob)
|
|
165
|
+
current_checksum=$(cat "$DISCUSSION_DIR"/*.md 2>/dev/null | md5sum | cut -d' ' -f1)
|
|
166
|
+
echo " discussions_checksum: \"$current_checksum\""
|
|
167
|
+
|
|
168
|
+
# Count concluded discussions
|
|
169
|
+
concluded_count=0
|
|
170
|
+
for file in "$DISCUSSION_DIR"/*.md; do
|
|
171
|
+
[ -f "$file" ] || continue
|
|
172
|
+
status=$(extract_field "$file" "status")
|
|
173
|
+
if [ "$status" = "concluded" ]; then
|
|
174
|
+
concluded_count=$((concluded_count + 1))
|
|
175
|
+
fi
|
|
176
|
+
done
|
|
177
|
+
echo " concluded_discussion_count: $concluded_count"
|
|
178
|
+
else
|
|
179
|
+
echo " discussions_checksum: null"
|
|
180
|
+
echo " concluded_discussion_count: 0"
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
echo ""
|
|
184
|
+
|
|
185
|
+
#
|
|
186
|
+
# CHECKSUM COMPARISON
|
|
187
|
+
#
|
|
188
|
+
echo "cache_validity:"
|
|
189
|
+
|
|
190
|
+
if [ -f "$CACHE_FILE" ]; then
|
|
191
|
+
cached_checksum=$(extract_field "$CACHE_FILE" "checksum")
|
|
192
|
+
|
|
193
|
+
if [ -d "$DISCUSSION_DIR" ] && [ -n "$(ls -A "$DISCUSSION_DIR" 2>/dev/null)" ]; then
|
|
194
|
+
current_checksum=$(cat "$DISCUSSION_DIR"/*.md 2>/dev/null | md5sum | cut -d' ' -f1)
|
|
195
|
+
|
|
196
|
+
if [ "$cached_checksum" = "$current_checksum" ]; then
|
|
197
|
+
echo " is_valid: true"
|
|
198
|
+
echo " reason: \"checksums match\""
|
|
199
|
+
else
|
|
200
|
+
echo " is_valid: false"
|
|
201
|
+
echo " reason: \"discussions have changed since cache was generated\""
|
|
202
|
+
fi
|
|
203
|
+
else
|
|
204
|
+
echo " is_valid: false"
|
|
205
|
+
echo " reason: \"no discussions to compare\""
|
|
206
|
+
fi
|
|
207
|
+
else
|
|
208
|
+
echo " is_valid: false"
|
|
209
|
+
echo " reason: \"no cache exists\""
|
|
210
|
+
fi
|