@leeovery/claude-technical-workflows 2.0.41 → 2.0.43
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/commands/workflow/start-discussion.md +162 -120
- package/commands/workflow/start-implementation.md +146 -66
- package/commands/workflow/start-planning.md +122 -43
- package/commands/workflow/start-research.md +90 -13
- package/commands/workflow/start-review.md +85 -41
- package/commands/workflow/start-specification.md +18 -11
- package/package.json +4 -2
- package/scripts/discovery-for-discussion.sh +198 -0
- package/scripts/discovery-for-implementation-and-review.sh +127 -0
- package/scripts/discovery-for-planning.sh +182 -0
- package/scripts/{specification-discovery.sh → discovery-for-specification.sh} +7 -2
- package/scripts/migrations/002-specification-frontmatter.sh +170 -0
- package/scripts/migrations/003-planning-frontmatter.sh +172 -0
- package/skills/technical-planning/references/output-local-markdown.md +17 -5
- package/skills/technical-research/SKILL.md +10 -1
- package/skills/technical-research/references/template.md +39 -0
- package/skills/technical-specification/references/specification-guide.md +20 -11
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# Discovers the current state of specifications and plans
|
|
4
|
+
# for the /start-planning command.
|
|
5
|
+
#
|
|
6
|
+
# Outputs structured YAML that the command can consume directly.
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
set -eo pipefail
|
|
10
|
+
|
|
11
|
+
SPEC_DIR="docs/workflow/specification"
|
|
12
|
+
PLAN_DIR="docs/workflow/planning"
|
|
13
|
+
|
|
14
|
+
# Helper: Extract a frontmatter field value from a file
|
|
15
|
+
# Usage: extract_field <file> <field_name>
|
|
16
|
+
extract_field() {
|
|
17
|
+
local file="$1"
|
|
18
|
+
local field="$2"
|
|
19
|
+
local value=""
|
|
20
|
+
|
|
21
|
+
# Extract from YAML frontmatter (file must start with ---)
|
|
22
|
+
if head -1 "$file" 2>/dev/null | grep -q "^---$"; then
|
|
23
|
+
value=$(sed -n '2,/^---$/p' "$file" 2>/dev/null | \
|
|
24
|
+
grep -i -m1 "^${field}:" | \
|
|
25
|
+
sed -E "s/^${field}:[[:space:]]*//i" || true)
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
echo "$value"
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# Start YAML output
|
|
32
|
+
echo "# Planning Command State Discovery"
|
|
33
|
+
echo "# Generated: $(date -Iseconds)"
|
|
34
|
+
echo ""
|
|
35
|
+
|
|
36
|
+
#
|
|
37
|
+
# SPECIFICATIONS
|
|
38
|
+
#
|
|
39
|
+
echo "specifications:"
|
|
40
|
+
|
|
41
|
+
feature_count=0
|
|
42
|
+
feature_ready_count=0
|
|
43
|
+
crosscutting_count=0
|
|
44
|
+
|
|
45
|
+
if [ -d "$SPEC_DIR" ] && [ -n "$(ls -A "$SPEC_DIR" 2>/dev/null)" ]; then
|
|
46
|
+
echo " exists: true"
|
|
47
|
+
echo " feature:"
|
|
48
|
+
|
|
49
|
+
# First pass: feature specifications
|
|
50
|
+
for file in "$SPEC_DIR"/*.md; do
|
|
51
|
+
[ -f "$file" ] || continue
|
|
52
|
+
|
|
53
|
+
name=$(basename "$file" .md)
|
|
54
|
+
status=$(extract_field "$file" "status")
|
|
55
|
+
status=${status:-"active"}
|
|
56
|
+
spec_type=$(extract_field "$file" "type")
|
|
57
|
+
spec_type=${spec_type:-"feature"}
|
|
58
|
+
|
|
59
|
+
# Skip cross-cutting specs in this pass
|
|
60
|
+
[ "$spec_type" = "cross-cutting" ] && continue
|
|
61
|
+
|
|
62
|
+
# Check if plan exists
|
|
63
|
+
has_plan="false"
|
|
64
|
+
if [ -f "$PLAN_DIR/${name}.md" ]; then
|
|
65
|
+
has_plan="true"
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
echo " - name: \"$name\""
|
|
69
|
+
echo " status: \"$status\""
|
|
70
|
+
echo " has_plan: $has_plan"
|
|
71
|
+
|
|
72
|
+
feature_count=$((feature_count + 1))
|
|
73
|
+
# "concluded" specs without plans are ready for planning
|
|
74
|
+
if [ "$status" = "concluded" ] && [ "$has_plan" = "false" ]; then
|
|
75
|
+
feature_ready_count=$((feature_ready_count + 1))
|
|
76
|
+
fi
|
|
77
|
+
done
|
|
78
|
+
|
|
79
|
+
if [ "$feature_count" -eq 0 ]; then
|
|
80
|
+
echo " [] # No feature specifications"
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
echo " crosscutting:"
|
|
84
|
+
|
|
85
|
+
# Second pass: cross-cutting specifications
|
|
86
|
+
for file in "$SPEC_DIR"/*.md; do
|
|
87
|
+
[ -f "$file" ] || continue
|
|
88
|
+
|
|
89
|
+
name=$(basename "$file" .md)
|
|
90
|
+
spec_type=$(extract_field "$file" "type")
|
|
91
|
+
spec_type=${spec_type:-"feature"}
|
|
92
|
+
|
|
93
|
+
# Only cross-cutting specs in this pass
|
|
94
|
+
[ "$spec_type" != "cross-cutting" ] && continue
|
|
95
|
+
|
|
96
|
+
status=$(extract_field "$file" "status")
|
|
97
|
+
status=${status:-"active"}
|
|
98
|
+
|
|
99
|
+
echo " - name: \"$name\""
|
|
100
|
+
echo " status: \"$status\""
|
|
101
|
+
|
|
102
|
+
crosscutting_count=$((crosscutting_count + 1))
|
|
103
|
+
done
|
|
104
|
+
|
|
105
|
+
if [ "$crosscutting_count" -eq 0 ]; then
|
|
106
|
+
echo " [] # No cross-cutting specifications"
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
echo " counts:"
|
|
110
|
+
echo " feature: $feature_count"
|
|
111
|
+
echo " feature_ready: $feature_ready_count"
|
|
112
|
+
echo " crosscutting: $crosscutting_count"
|
|
113
|
+
else
|
|
114
|
+
echo " exists: false"
|
|
115
|
+
echo " feature: []"
|
|
116
|
+
echo " crosscutting: []"
|
|
117
|
+
echo " counts:"
|
|
118
|
+
echo " feature: 0"
|
|
119
|
+
echo " feature_ready: 0"
|
|
120
|
+
echo " crosscutting: 0"
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
echo ""
|
|
124
|
+
|
|
125
|
+
#
|
|
126
|
+
# PLANS
|
|
127
|
+
#
|
|
128
|
+
echo "plans:"
|
|
129
|
+
|
|
130
|
+
if [ -d "$PLAN_DIR" ] && [ -n "$(ls -A "$PLAN_DIR" 2>/dev/null)" ]; then
|
|
131
|
+
echo " exists: true"
|
|
132
|
+
echo " files:"
|
|
133
|
+
|
|
134
|
+
for file in "$PLAN_DIR"/*.md; do
|
|
135
|
+
[ -f "$file" ] || continue
|
|
136
|
+
|
|
137
|
+
name=$(basename "$file" .md)
|
|
138
|
+
format=$(extract_field "$file" "format")
|
|
139
|
+
format=${format:-"local-markdown"}
|
|
140
|
+
status=$(extract_field "$file" "status")
|
|
141
|
+
status=${status:-"unknown"}
|
|
142
|
+
|
|
143
|
+
echo " - name: \"$name\""
|
|
144
|
+
echo " format: \"$format\""
|
|
145
|
+
echo " status: \"$status\""
|
|
146
|
+
done
|
|
147
|
+
else
|
|
148
|
+
echo " exists: false"
|
|
149
|
+
echo " files: []"
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
echo ""
|
|
153
|
+
|
|
154
|
+
#
|
|
155
|
+
# WORKFLOW STATE SUMMARY
|
|
156
|
+
#
|
|
157
|
+
echo "state:"
|
|
158
|
+
|
|
159
|
+
specs_exist="false"
|
|
160
|
+
plans_exist="false"
|
|
161
|
+
|
|
162
|
+
if [ -d "$SPEC_DIR" ] && [ -n "$(ls -A "$SPEC_DIR" 2>/dev/null)" ]; then
|
|
163
|
+
specs_exist="true"
|
|
164
|
+
fi
|
|
165
|
+
|
|
166
|
+
if [ -d "$PLAN_DIR" ] && [ -n "$(ls -A "$PLAN_DIR" 2>/dev/null)" ]; then
|
|
167
|
+
plans_exist="true"
|
|
168
|
+
fi
|
|
169
|
+
|
|
170
|
+
echo " has_specifications: $specs_exist"
|
|
171
|
+
echo " has_plans: $plans_exist"
|
|
172
|
+
|
|
173
|
+
# Determine workflow state for routing
|
|
174
|
+
if [ "$specs_exist" = "false" ]; then
|
|
175
|
+
echo " scenario: \"no_specs\""
|
|
176
|
+
elif [ "$feature_ready_count" -eq 0 ]; then
|
|
177
|
+
echo " scenario: \"no_ready_specs\""
|
|
178
|
+
elif [ "$feature_ready_count" -eq 1 ]; then
|
|
179
|
+
echo " scenario: \"single_ready_spec\""
|
|
180
|
+
else
|
|
181
|
+
echo " scenario: \"multiple_ready_specs\""
|
|
182
|
+
fi
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
#
|
|
3
|
-
# discover-spec-state.sh
|
|
4
|
-
#
|
|
5
3
|
# Discovers the current state of discussions, specifications, and cache
|
|
6
4
|
# for the /start-specification command.
|
|
7
5
|
#
|
|
@@ -68,13 +66,20 @@ if [ -d "$DISCUSSION_DIR" ] && [ -n "$(ls -A "$DISCUSSION_DIR" 2>/dev/null)" ];
|
|
|
68
66
|
|
|
69
67
|
# Check if this discussion has a corresponding individual spec
|
|
70
68
|
has_individual_spec="false"
|
|
69
|
+
spec_status=""
|
|
71
70
|
if [ -f "$SPEC_DIR/${name}.md" ]; then
|
|
72
71
|
has_individual_spec="true"
|
|
72
|
+
# Extract spec status in real-time (not from cache)
|
|
73
|
+
spec_status=$(extract_field "$SPEC_DIR/${name}.md" "status")
|
|
74
|
+
spec_status=${spec_status:-"in-progress"}
|
|
73
75
|
fi
|
|
74
76
|
|
|
75
77
|
echo " - name: \"$name\""
|
|
76
78
|
echo " status: \"$status\""
|
|
77
79
|
echo " has_individual_spec: $has_individual_spec"
|
|
80
|
+
if [ "$has_individual_spec" = "true" ]; then
|
|
81
|
+
echo " spec_status: \"$spec_status\""
|
|
82
|
+
fi
|
|
78
83
|
done
|
|
79
84
|
else
|
|
80
85
|
echo " [] # No discussions found"
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# 002-specification-frontmatter.sh
|
|
4
|
+
#
|
|
5
|
+
# Migrates specification documents from legacy markdown header format to YAML frontmatter.
|
|
6
|
+
#
|
|
7
|
+
# Legacy format:
|
|
8
|
+
# # Specification: {Topic}
|
|
9
|
+
#
|
|
10
|
+
# **Status**: Building specification | Complete
|
|
11
|
+
# **Type**: feature | cross-cutting
|
|
12
|
+
# **Last Updated**: YYYY-MM-DD
|
|
13
|
+
#
|
|
14
|
+
# New format:
|
|
15
|
+
# ---
|
|
16
|
+
# topic: {topic-name}
|
|
17
|
+
# status: in-progress | concluded
|
|
18
|
+
# type: feature | cross-cutting | (empty if unknown)
|
|
19
|
+
# date: YYYY-MM-DD
|
|
20
|
+
# ---
|
|
21
|
+
#
|
|
22
|
+
# # Specification: {Topic}
|
|
23
|
+
#
|
|
24
|
+
# Status mapping (normalized across all document types):
|
|
25
|
+
# Building specification, Building, Draft → in-progress
|
|
26
|
+
# Complete, Completed, Done → concluded
|
|
27
|
+
#
|
|
28
|
+
# Type handling:
|
|
29
|
+
# feature → feature
|
|
30
|
+
# cross-cutting → cross-cutting
|
|
31
|
+
# (not found or unrecognized) → empty (requires manual review)
|
|
32
|
+
#
|
|
33
|
+
# This script is sourced by migrate.sh and has access to:
|
|
34
|
+
# - is_migrated "filepath" "migration_id"
|
|
35
|
+
# - record_migration "filepath" "migration_id"
|
|
36
|
+
# - report_update "filepath" "description"
|
|
37
|
+
# - report_skip "filepath"
|
|
38
|
+
#
|
|
39
|
+
|
|
40
|
+
MIGRATION_ID="002"
|
|
41
|
+
SPEC_DIR="docs/workflow/specification"
|
|
42
|
+
|
|
43
|
+
# Skip if no specification directory
|
|
44
|
+
if [ ! -d "$SPEC_DIR" ]; then
|
|
45
|
+
return 0
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# Process each specification file
|
|
49
|
+
for file in "$SPEC_DIR"/*.md; do
|
|
50
|
+
[ -f "$file" ] || continue
|
|
51
|
+
|
|
52
|
+
# Check if already migrated via tracking
|
|
53
|
+
if is_migrated "$file" "$MIGRATION_ID"; then
|
|
54
|
+
report_skip "$file"
|
|
55
|
+
continue
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# Check if file already has YAML frontmatter
|
|
59
|
+
if head -1 "$file" 2>/dev/null | grep -q "^---$"; then
|
|
60
|
+
# Already has frontmatter - just record and skip
|
|
61
|
+
record_migration "$file" "$MIGRATION_ID"
|
|
62
|
+
report_skip "$file"
|
|
63
|
+
continue
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
# Check if file has legacy format (look for **Status**: or **Status:** or **Type**: or **Last Updated**:)
|
|
67
|
+
if ! grep -q '^\*\*Status\*\*:\|^\*\*Status:\*\*\|^\*\*Type\*\*:\|^\*\*Last Updated\*\*:' "$file" 2>/dev/null; then
|
|
68
|
+
# No legacy format found - might be malformed, skip
|
|
69
|
+
record_migration "$file" "$MIGRATION_ID"
|
|
70
|
+
report_skip "$file"
|
|
71
|
+
continue
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
#
|
|
75
|
+
# Extract values from legacy format
|
|
76
|
+
#
|
|
77
|
+
|
|
78
|
+
# Use filename as topic (canonical identifier throughout the workflow)
|
|
79
|
+
topic_kebab=$(basename "$file" .md)
|
|
80
|
+
|
|
81
|
+
# Extract status from **Status**: Value or **Status:** Value
|
|
82
|
+
# Handle variations: "Building specification", "Building", "Complete", "Completed", etc.
|
|
83
|
+
status_raw=$(grep -m1 '^\*\*Status\*\*:\|^\*\*Status:\*\*' "$file" | \
|
|
84
|
+
sed 's/^\*\*Status\*\*:[[:space:]]*//' | \
|
|
85
|
+
sed 's/^\*\*Status:\*\*[[:space:]]*//' | \
|
|
86
|
+
tr '[:upper:]' '[:lower:]' | \
|
|
87
|
+
xargs)
|
|
88
|
+
|
|
89
|
+
# Map legacy status to normalized values (consistent across all document types)
|
|
90
|
+
case "$status_raw" in
|
|
91
|
+
"building specification"|"building"|"draft"|"in progress"|"in-progress"|"exploring"|"deciding")
|
|
92
|
+
status_new="in-progress"
|
|
93
|
+
;;
|
|
94
|
+
"complete"|"completed"|"done"|"finished"|"concluded")
|
|
95
|
+
status_new="concluded"
|
|
96
|
+
;;
|
|
97
|
+
*)
|
|
98
|
+
status_new="in-progress" # Default for unknown
|
|
99
|
+
;;
|
|
100
|
+
esac
|
|
101
|
+
|
|
102
|
+
# Extract type from **Type**: Value (may not exist in legacy files)
|
|
103
|
+
type_raw=$(grep -m1 '^\*\*Type\*\*:\|^\*\*Type:\*\*' "$file" 2>/dev/null | \
|
|
104
|
+
sed 's/^\*\*Type\*\*:[[:space:]]*//' | \
|
|
105
|
+
sed 's/^\*\*Type:\*\*[[:space:]]*//' | \
|
|
106
|
+
tr '[:upper:]' '[:lower:]' | \
|
|
107
|
+
xargs || echo "")
|
|
108
|
+
|
|
109
|
+
# Normalize type (leave empty if not found or unrecognized - requires manual review)
|
|
110
|
+
case "$type_raw" in
|
|
111
|
+
"feature")
|
|
112
|
+
type_new="feature"
|
|
113
|
+
;;
|
|
114
|
+
"cross-cutting"|"crosscutting"|"cross cutting")
|
|
115
|
+
type_new="cross-cutting"
|
|
116
|
+
;;
|
|
117
|
+
*)
|
|
118
|
+
type_new="" # Empty - requires manual classification
|
|
119
|
+
;;
|
|
120
|
+
esac
|
|
121
|
+
|
|
122
|
+
# Extract date from **Last Updated**: YYYY-MM-DD or **Date**: YYYY-MM-DD
|
|
123
|
+
date_value=$(grep -m1 '^\*\*Last Updated\*\*:\|^\*\*Date\*\*:' "$file" | \
|
|
124
|
+
grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2}' || echo "")
|
|
125
|
+
|
|
126
|
+
# Use today's date if none found
|
|
127
|
+
if [ -z "$date_value" ]; then
|
|
128
|
+
date_value=$(date +%Y-%m-%d)
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
#
|
|
132
|
+
# Build new file content
|
|
133
|
+
#
|
|
134
|
+
|
|
135
|
+
# Create frontmatter
|
|
136
|
+
frontmatter="---
|
|
137
|
+
topic: $topic_kebab
|
|
138
|
+
status: $status_new
|
|
139
|
+
type: $type_new
|
|
140
|
+
date: $date_value
|
|
141
|
+
---"
|
|
142
|
+
|
|
143
|
+
# Extract H1 heading (preserve original)
|
|
144
|
+
h1_heading=$(grep -m1 "^# " "$file")
|
|
145
|
+
|
|
146
|
+
# Find line number of first ## heading (start of real content after metadata)
|
|
147
|
+
first_section_line=$(grep -n "^## " "$file" | head -1 | cut -d: -f1)
|
|
148
|
+
|
|
149
|
+
# Get content from first ## onwards (preserves all content)
|
|
150
|
+
if [ -n "$first_section_line" ]; then
|
|
151
|
+
content=$(tail -n +$first_section_line "$file")
|
|
152
|
+
else
|
|
153
|
+
# No ## found - take everything after the metadata block
|
|
154
|
+
# Find first blank line after the metadata, then take from there
|
|
155
|
+
content=""
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
# Write new content: frontmatter + H1 + blank line + content
|
|
159
|
+
{
|
|
160
|
+
echo "$frontmatter"
|
|
161
|
+
echo ""
|
|
162
|
+
echo "$h1_heading"
|
|
163
|
+
echo ""
|
|
164
|
+
echo "$content"
|
|
165
|
+
} > "$file"
|
|
166
|
+
|
|
167
|
+
# Record and report
|
|
168
|
+
record_migration "$file" "$MIGRATION_ID"
|
|
169
|
+
report_update "$file" "added frontmatter (status: $status_new, type: $type_new)"
|
|
170
|
+
done
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# 003-planning-frontmatter.sh
|
|
4
|
+
#
|
|
5
|
+
# Migrates plan documents from legacy format to full YAML frontmatter.
|
|
6
|
+
#
|
|
7
|
+
# Legacy format (partial frontmatter + inline metadata):
|
|
8
|
+
# ---
|
|
9
|
+
# format: local-markdown
|
|
10
|
+
# ---
|
|
11
|
+
#
|
|
12
|
+
# # Implementation Plan: {Feature/Project Name}
|
|
13
|
+
#
|
|
14
|
+
# **Date**: YYYY-MM-DD
|
|
15
|
+
# **Status**: Draft | Ready | In Progress | Completed
|
|
16
|
+
# **Specification**: `docs/workflow/specification/{topic}.md`
|
|
17
|
+
#
|
|
18
|
+
# New format (all metadata in frontmatter):
|
|
19
|
+
# ---
|
|
20
|
+
# topic: {topic-name}
|
|
21
|
+
# status: in-progress | concluded
|
|
22
|
+
# date: YYYY-MM-DD
|
|
23
|
+
# format: local-markdown
|
|
24
|
+
# specification: {topic}.md
|
|
25
|
+
# ---
|
|
26
|
+
#
|
|
27
|
+
# # Implementation Plan: {Feature/Project Name}
|
|
28
|
+
#
|
|
29
|
+
# Status mapping (normalized across all document types):
|
|
30
|
+
# Draft, Ready, In Progress → in-progress
|
|
31
|
+
# Completed, Done → concluded
|
|
32
|
+
#
|
|
33
|
+
# This script is sourced by migrate.sh and has access to:
|
|
34
|
+
# - is_migrated "filepath" "migration_id"
|
|
35
|
+
# - record_migration "filepath" "migration_id"
|
|
36
|
+
# - report_update "filepath" "description"
|
|
37
|
+
# - report_skip "filepath"
|
|
38
|
+
#
|
|
39
|
+
|
|
40
|
+
MIGRATION_ID="003"
|
|
41
|
+
PLAN_DIR="docs/workflow/planning"
|
|
42
|
+
|
|
43
|
+
# Skip if no planning directory
|
|
44
|
+
if [ ! -d "$PLAN_DIR" ]; then
|
|
45
|
+
return 0
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# Process each plan file
|
|
49
|
+
for file in "$PLAN_DIR"/*.md; do
|
|
50
|
+
[ -f "$file" ] || continue
|
|
51
|
+
|
|
52
|
+
# Check if already migrated via tracking
|
|
53
|
+
if is_migrated "$file" "$MIGRATION_ID"; then
|
|
54
|
+
report_skip "$file"
|
|
55
|
+
continue
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# Check if file already has full frontmatter (topic field present)
|
|
59
|
+
if head -10 "$file" 2>/dev/null | grep -q "^topic:"; then
|
|
60
|
+
# Already has full frontmatter - just record and skip
|
|
61
|
+
record_migration "$file" "$MIGRATION_ID"
|
|
62
|
+
report_skip "$file"
|
|
63
|
+
continue
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
# Check if file has legacy format indicators
|
|
67
|
+
# Legacy format has partial frontmatter (format:) OR inline **Date**/**Status**/**Specification**
|
|
68
|
+
has_partial_frontmatter=$(head -5 "$file" 2>/dev/null | grep -c "^format:" || true)
|
|
69
|
+
has_inline_metadata=$(grep -c '^\*\*Date\*\*:\|^\*\*Status\*\*:\|^\*\*Specification\*\*:' "$file" 2>/dev/null || true)
|
|
70
|
+
|
|
71
|
+
if [ "${has_partial_frontmatter:-0}" = "0" ] && [ "${has_inline_metadata:-0}" = "0" ]; then
|
|
72
|
+
# No legacy format found - might be malformed, skip
|
|
73
|
+
record_migration "$file" "$MIGRATION_ID"
|
|
74
|
+
report_skip "$file"
|
|
75
|
+
continue
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
#
|
|
79
|
+
# Extract values from legacy format
|
|
80
|
+
#
|
|
81
|
+
|
|
82
|
+
# Use filename as topic (canonical identifier throughout the workflow)
|
|
83
|
+
topic_kebab=$(basename "$file" .md)
|
|
84
|
+
|
|
85
|
+
# Extract format from existing frontmatter (if present)
|
|
86
|
+
format_value=$(sed -n '/^---$/,/^---$/p' "$file" 2>/dev/null | grep "^format:" | sed 's/^format:[[:space:]]*//' | xargs || echo "")
|
|
87
|
+
if [ -z "$format_value" ]; then
|
|
88
|
+
format_value="local-markdown" # Default format
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
# Extract status from **Status**: Value
|
|
92
|
+
status_raw=$(grep -m1 '^\*\*Status\*\*:' "$file" 2>/dev/null | \
|
|
93
|
+
sed 's/^\*\*Status\*\*:[[:space:]]*//' | \
|
|
94
|
+
tr '[:upper:]' '[:lower:]' | \
|
|
95
|
+
xargs || echo "")
|
|
96
|
+
|
|
97
|
+
# Map legacy status to normalized values
|
|
98
|
+
case "$status_raw" in
|
|
99
|
+
"draft"|"ready"|"in progress"|"in-progress")
|
|
100
|
+
status_new="in-progress"
|
|
101
|
+
;;
|
|
102
|
+
"completed"|"complete"|"done"|"concluded")
|
|
103
|
+
status_new="concluded"
|
|
104
|
+
;;
|
|
105
|
+
*)
|
|
106
|
+
status_new="in-progress" # Default for unknown
|
|
107
|
+
;;
|
|
108
|
+
esac
|
|
109
|
+
|
|
110
|
+
# Extract date from **Date**: YYYY-MM-DD
|
|
111
|
+
date_value=$(grep -m1 '^\*\*Date\*\*:' "$file" 2>/dev/null | \
|
|
112
|
+
grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2}' || echo "")
|
|
113
|
+
|
|
114
|
+
# Use today's date if none found
|
|
115
|
+
if [ -z "$date_value" ]; then
|
|
116
|
+
date_value=$(date +%Y-%m-%d)
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
# Extract specification from **Specification**: `docs/workflow/specification/{topic}.md`
|
|
120
|
+
# We just want the filename, not the full path
|
|
121
|
+
spec_raw=$(grep -m1 '^\*\*Specification\*\*:' "$file" 2>/dev/null | \
|
|
122
|
+
sed 's/^\*\*Specification\*\*:[[:space:]]*//' | \
|
|
123
|
+
sed 's/`//g' | \
|
|
124
|
+
xargs || echo "")
|
|
125
|
+
|
|
126
|
+
# Extract just the filename from the path
|
|
127
|
+
if [ -n "$spec_raw" ]; then
|
|
128
|
+
spec_value=$(basename "$spec_raw")
|
|
129
|
+
else
|
|
130
|
+
spec_value="${topic_kebab}.md" # Default to topic name
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
#
|
|
134
|
+
# Build new file content
|
|
135
|
+
#
|
|
136
|
+
|
|
137
|
+
# Create frontmatter
|
|
138
|
+
frontmatter="---
|
|
139
|
+
topic: $topic_kebab
|
|
140
|
+
status: $status_new
|
|
141
|
+
date: $date_value
|
|
142
|
+
format: $format_value
|
|
143
|
+
specification: $spec_value
|
|
144
|
+
---"
|
|
145
|
+
|
|
146
|
+
# Extract H1 heading (preserve original)
|
|
147
|
+
h1_heading=$(grep -m1 "^# " "$file")
|
|
148
|
+
|
|
149
|
+
# Find line number of first ## heading (start of real content after metadata)
|
|
150
|
+
first_section_line=$(grep -n "^## " "$file" | head -1 | cut -d: -f1)
|
|
151
|
+
|
|
152
|
+
# Get content from first ## onwards (preserves all content)
|
|
153
|
+
if [ -n "$first_section_line" ]; then
|
|
154
|
+
content=$(tail -n +$first_section_line "$file")
|
|
155
|
+
else
|
|
156
|
+
# No ## found - might be empty or malformed
|
|
157
|
+
content=""
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
# Write new content: frontmatter + H1 + blank line + content
|
|
161
|
+
{
|
|
162
|
+
echo "$frontmatter"
|
|
163
|
+
echo ""
|
|
164
|
+
echo "$h1_heading"
|
|
165
|
+
echo ""
|
|
166
|
+
echo "$content"
|
|
167
|
+
} > "$file"
|
|
168
|
+
|
|
169
|
+
# Record and report
|
|
170
|
+
record_migration "$file" "$MIGRATION_ID"
|
|
171
|
+
report_update "$file" "added full frontmatter (status: $status_new, format: $format_value)"
|
|
172
|
+
done
|
|
@@ -33,15 +33,15 @@ Create `{topic}.md` with this structure:
|
|
|
33
33
|
|
|
34
34
|
```markdown
|
|
35
35
|
---
|
|
36
|
+
topic: {feature-name}
|
|
37
|
+
status: in-progress
|
|
38
|
+
date: YYYY-MM-DD
|
|
36
39
|
format: local-markdown
|
|
40
|
+
specification: {topic}.md
|
|
37
41
|
---
|
|
38
42
|
|
|
39
43
|
# Implementation Plan: {Feature/Project Name}
|
|
40
44
|
|
|
41
|
-
**Date**: YYYY-MM-DD *(use today's actual date)*
|
|
42
|
-
**Status**: Draft | Ready | In Progress | Completed
|
|
43
|
-
**Specification**: `docs/workflow/specification/{topic}.md`
|
|
44
|
-
|
|
45
45
|
## Overview
|
|
46
46
|
|
|
47
47
|
**Goal**: What we're building and why (one sentence)
|
|
@@ -222,7 +222,19 @@ grep -A 5 "### Phase 1" docs/workflow/planning/billing-system.md | grep "\[x\]"
|
|
|
222
222
|
|
|
223
223
|
## Frontmatter
|
|
224
224
|
|
|
225
|
-
|
|
225
|
+
Plan documents use YAML frontmatter for metadata:
|
|
226
|
+
|
|
227
|
+
```yaml
|
|
228
|
+
---
|
|
229
|
+
topic: {feature-name} # Matches filename (without .md)
|
|
230
|
+
status: in-progress # in-progress | concluded
|
|
231
|
+
date: YYYY-MM-DD # Creation date
|
|
232
|
+
format: local-markdown # Output format used
|
|
233
|
+
specification: {topic}.md # Source specification filename
|
|
234
|
+
---
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
The `format` field tells implementation which output adapter to use for reading/updating the plan.
|
|
226
238
|
|
|
227
239
|
## Flagging Incomplete Tasks
|
|
228
240
|
|
|
@@ -58,9 +58,18 @@ Ask one question at a time. Wait for the answer. Document. Then ask the next.
|
|
|
58
58
|
|
|
59
59
|
**Output**: `docs/workflow/research/exploration.md`
|
|
60
60
|
|
|
61
|
+
**Template**: Use `references/template.md` for document structure. All research documents use YAML frontmatter:
|
|
62
|
+
|
|
63
|
+
```yaml
|
|
64
|
+
---
|
|
65
|
+
topic: exploration
|
|
66
|
+
date: YYYY-MM-DD # Use today's actual date
|
|
67
|
+
---
|
|
68
|
+
```
|
|
69
|
+
|
|
61
70
|
Start with one file. Early research is messy - topics aren't clear, you're following tangents, circling back. Don't force structure too early.
|
|
62
71
|
|
|
63
|
-
**Let themes emerge**: Over multiple sessions, topics may become distinct. When they do, split into semantic files (`market-landscape.md`, `technical-feasibility.md`).
|
|
72
|
+
**Let themes emerge**: Over multiple sessions, topics may become distinct. When they do, split into semantic files (`market-landscape.md`, `technical-feasibility.md`). Update the `topic` field to match the filename.
|
|
64
73
|
|
|
65
74
|
**Periodic review**: Every few sessions, assess: are themes emerging? Split them out. Still fuzzy? Keep exploring. Ready for deeper discussion or specification? Research is complete.
|
|
66
75
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Research Document Template
|
|
2
|
+
|
|
3
|
+
Use this template when creating research documents.
|
|
4
|
+
|
|
5
|
+
## Template
|
|
6
|
+
|
|
7
|
+
```markdown
|
|
8
|
+
---
|
|
9
|
+
topic: exploration
|
|
10
|
+
date: YYYY-MM-DD # Use today's actual date
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Research: {Title}
|
|
14
|
+
|
|
15
|
+
Brief description of what this research covers and what prompted it.
|
|
16
|
+
|
|
17
|
+
## Starting Point
|
|
18
|
+
|
|
19
|
+
What we know so far:
|
|
20
|
+
- {Initial thoughts or context from the user}
|
|
21
|
+
- {Any constraints or existing knowledge}
|
|
22
|
+
- {Where we're starting: technical, market, business, etc.}
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
{Content follows - freeform, managed by the skill}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Field Notes
|
|
30
|
+
|
|
31
|
+
- `topic`: Use `exploration` for the initial exploration file. Use semantic names (`market-landscape`, `technical-feasibility`) when splitting into focused files.
|
|
32
|
+
- `date`: Today's date when creating the document.
|
|
33
|
+
|
|
34
|
+
## Notes
|
|
35
|
+
|
|
36
|
+
- The "Starting Point" section captures context from the initial conversation
|
|
37
|
+
- Content after that is intentionally unstructured - let themes emerge naturally
|
|
38
|
+
- When splitting into topic files, update the `topic` field to match the filename
|
|
39
|
+
- The skill handles content organization during sessions
|