create-battle-plan 1.0.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/bin/cli.js +434 -0
- package/package.json +31 -0
- package/template/.cascaderc.example +6 -0
- package/template/.claude/commands/distill.md +186 -0
- package/template/.claude/commands/good-morning.md +74 -0
- package/template/.claude/commands/wrap-up.md +61 -0
- package/template/.claude/settings.json +3 -0
- package/template/.githooks/pre-commit +41 -0
- package/template/CLAUDE.md +154 -0
- package/template/docs/README.md +62 -0
- package/template/tools/check-metrics.sh +91 -0
- package/template/tools/init-project.sh +245 -0
- package/template/tools/setup-hooks.sh +14 -0
- package/template/tools/sync-metrics.sh +82 -0
- package/template/tools/touch-date.sh +31 -0
- package/template/tools/verify-cascade.sh +154 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# init-project.sh — Scaffolds a new project from onboarding wizard answers.
|
|
3
|
+
# Called by the LLM after the 5-question interview.
|
|
4
|
+
#
|
|
5
|
+
# Usage: tools/init-project.sh \
|
|
6
|
+
# --name "Project Name" \
|
|
7
|
+
# --horizon "3 weeks" \
|
|
8
|
+
# --metrics "metric1,metric2,metric3" \
|
|
9
|
+
# --domains "market,validation,strategy" \
|
|
10
|
+
# --people "Name1:Role1,Name2:Role2"
|
|
11
|
+
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
|
|
14
|
+
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
15
|
+
|
|
16
|
+
# Parse arguments
|
|
17
|
+
PROJECT_NAME=""
|
|
18
|
+
HORIZON=""
|
|
19
|
+
METRICS=""
|
|
20
|
+
DOMAINS=""
|
|
21
|
+
PEOPLE=""
|
|
22
|
+
|
|
23
|
+
while [[ $# -gt 0 ]]; do
|
|
24
|
+
case $1 in
|
|
25
|
+
--name) PROJECT_NAME="$2"; shift 2 ;;
|
|
26
|
+
--horizon) HORIZON="$2"; shift 2 ;;
|
|
27
|
+
--metrics) METRICS="$2"; shift 2 ;;
|
|
28
|
+
--domains) DOMAINS="$2"; shift 2 ;;
|
|
29
|
+
--people) PEOPLE="$2"; shift 2 ;;
|
|
30
|
+
*) echo "Unknown option: $1"; exit 1 ;;
|
|
31
|
+
esac
|
|
32
|
+
done
|
|
33
|
+
|
|
34
|
+
if [ -z "$PROJECT_NAME" ] || [ -z "$METRICS" ] || [ -z "$DOMAINS" ]; then
|
|
35
|
+
echo "ERROR: --name, --metrics, and --domains are required."
|
|
36
|
+
echo "Usage: tools/init-project.sh --name \"...\" --horizon \"...\" --metrics \"m1,m2\" --domains \"d1,d2\" --people \"N1:R1,N2:R2\""
|
|
37
|
+
exit 1
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
TODAY=$(date +%Y-%m-%d)
|
|
41
|
+
|
|
42
|
+
echo "=== Initializing project: $PROJECT_NAME ==="
|
|
43
|
+
|
|
44
|
+
# Step 1: Move demo content to examples/
|
|
45
|
+
echo "Moving demo content to examples/startup-validation/..."
|
|
46
|
+
mkdir -p "$REPO_ROOT/examples/startup-validation"
|
|
47
|
+
|
|
48
|
+
if [ -d "$REPO_ROOT/docs" ]; then
|
|
49
|
+
# Preserve the docs/README.md (vault rules) — it's generic
|
|
50
|
+
cp "$REPO_ROOT/docs/README.md" /tmp/cascade-vault-rules.md 2>/dev/null || true
|
|
51
|
+
|
|
52
|
+
# Move all docs to examples
|
|
53
|
+
cp -r "$REPO_ROOT/docs/"* "$REPO_ROOT/examples/startup-validation/" 2>/dev/null || true
|
|
54
|
+
rm -rf "$REPO_ROOT/docs/"*/
|
|
55
|
+
rm -f "$REPO_ROOT/docs/"*.md
|
|
56
|
+
|
|
57
|
+
# Restore vault rules
|
|
58
|
+
cp /tmp/cascade-vault-rules.md "$REPO_ROOT/docs/README.md" 2>/dev/null || true
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# Also preserve the demo metrics.yml
|
|
62
|
+
cp "$REPO_ROOT/metrics.yml" "$REPO_ROOT/examples/startup-validation/metrics.yml" 2>/dev/null || true
|
|
63
|
+
|
|
64
|
+
# Step 2: Create domain directories and initial docs
|
|
65
|
+
IFS=',' read -ra DOMAIN_ARRAY <<< "$DOMAINS"
|
|
66
|
+
for domain in "${DOMAIN_ARRAY[@]}"; do
|
|
67
|
+
domain=$(echo "$domain" | tr -d ' ')
|
|
68
|
+
mkdir -p "$REPO_ROOT/docs/$domain"
|
|
69
|
+
|
|
70
|
+
# Create an initial doc for each domain
|
|
71
|
+
cat > "$REPO_ROOT/docs/$domain/${domain}-overview.md" << DOCEOF
|
|
72
|
+
# ${domain^} Overview
|
|
73
|
+
|
|
74
|
+
**Last Updated:** $TODAY
|
|
75
|
+
**Status:** Draft
|
|
76
|
+
**Role:** cascade-target
|
|
77
|
+
**Compression:** amended
|
|
78
|
+
|
|
79
|
+
**TL;DR:** Initial ${domain} document for $PROJECT_NAME. To be filled in as the project progresses.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Notes
|
|
84
|
+
|
|
85
|
+
_Start adding content here._
|
|
86
|
+
DOCEOF
|
|
87
|
+
|
|
88
|
+
echo "Created: docs/$domain/${domain}-overview.md"
|
|
89
|
+
done
|
|
90
|
+
|
|
91
|
+
# Step 3: Create metrics.yml
|
|
92
|
+
echo "Creating metrics.yml..."
|
|
93
|
+
cat > "$REPO_ROOT/metrics.yml" << METRICSEOF
|
|
94
|
+
# metrics.yml — project-wide metrics registry for $PROJECT_NAME
|
|
95
|
+
# The LLM updates this file FIRST in any cascade, before touching docs.
|
|
96
|
+
# Scripts verify all (→ metrics.yml#field) references against these values.
|
|
97
|
+
|
|
98
|
+
last_updated: $TODAY
|
|
99
|
+
|
|
100
|
+
METRICSEOF
|
|
101
|
+
|
|
102
|
+
IFS=',' read -ra METRIC_ARRAY <<< "$METRICS"
|
|
103
|
+
for metric in "${METRIC_ARRAY[@]}"; do
|
|
104
|
+
metric_key=$(echo "$metric" | tr -d ' ' | tr '[:upper:]' '[:lower:]' | tr ' ' '_' | tr -cd 'a-z0-9_')
|
|
105
|
+
echo "${metric_key}: 0" >> "$REPO_ROOT/metrics.yml"
|
|
106
|
+
done
|
|
107
|
+
|
|
108
|
+
echo "Created: metrics.yml with ${#METRIC_ARRAY[@]} metrics"
|
|
109
|
+
|
|
110
|
+
# Step 4: Create battle plan
|
|
111
|
+
echo "Creating battle plan..."
|
|
112
|
+
cat > "$REPO_ROOT/docs/battle-plan.md" << BPEOF
|
|
113
|
+
# Battle Plan — $PROJECT_NAME
|
|
114
|
+
|
|
115
|
+
**Last Updated:** $TODAY
|
|
116
|
+
**Status:** Active
|
|
117
|
+
**Role:** source-of-truth
|
|
118
|
+
**Compression:** chronological
|
|
119
|
+
|
|
120
|
+
**TL;DR:** $PROJECT_NAME — just initialized. Time horizon: ${HORIZON:-"not set"}. All metrics at 0. First priority: fill in the battle plan with real tasks and targets.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Rules for This Document
|
|
125
|
+
|
|
126
|
+
1. Every task has an assigned date — no "sometime this week"
|
|
127
|
+
2. Tasks move, never disappear — if slipped, add new date + reason
|
|
128
|
+
3. New info updates the battle plan FIRST, before any other doc
|
|
129
|
+
4. Everything links — tasks reference the doc they depend on or produce
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Key Metrics
|
|
134
|
+
|
|
135
|
+
| Metric | Target | Current |
|
|
136
|
+
|--------|--------|---------|
|
|
137
|
+
BPEOF
|
|
138
|
+
|
|
139
|
+
for metric in "${METRIC_ARRAY[@]}"; do
|
|
140
|
+
metric_key=$(echo "$metric" | tr -d ' ' | tr '[:upper:]' '[:lower:]' | tr ' ' '_' | tr -cd 'a-z0-9_')
|
|
141
|
+
metric_display=$(echo "$metric" | sed 's/^[[:space:]]*//')
|
|
142
|
+
echo "| $metric_display | _set target_ | **0** (→ metrics.yml#${metric_key}) |" >> "$REPO_ROOT/docs/battle-plan.md"
|
|
143
|
+
done
|
|
144
|
+
|
|
145
|
+
cat >> "$REPO_ROOT/docs/battle-plan.md" << 'BPEOF2'
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Today's Priorities
|
|
150
|
+
|
|
151
|
+
- [ ] Set targets for each metric
|
|
152
|
+
- [ ] Fill in this week's tasks
|
|
153
|
+
- [ ] Record any existing conversations in external-insights.md
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## This Week
|
|
158
|
+
|
|
159
|
+
_Add day-by-day tasks here._
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Daily Log
|
|
164
|
+
|
|
165
|
+
_Append-only. Three lines per day._
|
|
166
|
+
BPEOF2
|
|
167
|
+
|
|
168
|
+
echo "Created: docs/battle-plan.md"
|
|
169
|
+
|
|
170
|
+
# Step 5: Create external-insights.md with people sections
|
|
171
|
+
echo "Creating external-insights.md..."
|
|
172
|
+
cat > "$REPO_ROOT/docs/external-insights.md" << EIEOF
|
|
173
|
+
# External Insights
|
|
174
|
+
|
|
175
|
+
**Last Updated:** $TODAY
|
|
176
|
+
**Status:** Active
|
|
177
|
+
**Role:** cascade-target
|
|
178
|
+
**Compression:** chronological
|
|
179
|
+
|
|
180
|
+
**TL;DR:** All external conversations, calls, and meetings for $PROJECT_NAME. 0 sessions recorded so far.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## How to Use This Document
|
|
185
|
+
|
|
186
|
+
Every conversation gets appended as a dated session. Record everything — even "small" chats contain signal.
|
|
187
|
+
|
|
188
|
+
### Template
|
|
189
|
+
|
|
190
|
+
\`\`\`markdown
|
|
191
|
+
## Session N (YYYY-MM-DD) — [Person Name], [Role/Company]
|
|
192
|
+
|
|
193
|
+
### Context
|
|
194
|
+
[Why this conversation happened]
|
|
195
|
+
|
|
196
|
+
### Key insights
|
|
197
|
+
1. **Insight title.** Detail. \`Confidence: [level]\`
|
|
198
|
+
|
|
199
|
+
### Raw quotes (if available)
|
|
200
|
+
> "Quote here"
|
|
201
|
+
|
|
202
|
+
### Action items
|
|
203
|
+
- [ ] Follow-up X
|
|
204
|
+
\`\`\`
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## People
|
|
209
|
+
|
|
210
|
+
EIEOF
|
|
211
|
+
|
|
212
|
+
if [ -n "$PEOPLE" ]; then
|
|
213
|
+
IFS=',' read -ra PEOPLE_ARRAY <<< "$PEOPLE"
|
|
214
|
+
for person in "${PEOPLE_ARRAY[@]}"; do
|
|
215
|
+
name=$(echo "$person" | cut -d: -f1 | sed 's/^[[:space:]]*//')
|
|
216
|
+
role=$(echo "$person" | cut -d: -f2 | sed 's/^[[:space:]]*//')
|
|
217
|
+
cat >> "$REPO_ROOT/docs/external-insights.md" << PERSONEOF
|
|
218
|
+
### $name — $role
|
|
219
|
+
_No sessions recorded yet._
|
|
220
|
+
|
|
221
|
+
PERSONEOF
|
|
222
|
+
echo " Added person: $name ($role)"
|
|
223
|
+
done
|
|
224
|
+
fi
|
|
225
|
+
|
|
226
|
+
echo "Created: docs/external-insights.md"
|
|
227
|
+
|
|
228
|
+
# Step 6: Install hooks
|
|
229
|
+
echo ""
|
|
230
|
+
"$REPO_ROOT/tools/setup-hooks.sh"
|
|
231
|
+
|
|
232
|
+
# Step 7: Create initialized flag
|
|
233
|
+
touch "$REPO_ROOT/.battle-plan-initialized"
|
|
234
|
+
|
|
235
|
+
echo ""
|
|
236
|
+
echo "=== Project initialized! ==="
|
|
237
|
+
echo "Project: $PROJECT_NAME"
|
|
238
|
+
echo "Domains: $DOMAINS"
|
|
239
|
+
echo "Metrics: $METRICS"
|
|
240
|
+
echo "Demo content preserved in: examples/startup-validation/"
|
|
241
|
+
echo ""
|
|
242
|
+
echo "Next steps:"
|
|
243
|
+
echo " 1. Open docs/battle-plan.md and set your targets"
|
|
244
|
+
echo " 2. Tell your LLM about your project context"
|
|
245
|
+
echo " 3. Start working — the cascade will keep docs in sync"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# setup-hooks.sh — Installs the Battle Plan git hooks.
|
|
3
|
+
# Usage: tools/setup-hooks.sh
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
8
|
+
|
|
9
|
+
git -C "$REPO_ROOT" config core.hooksPath .githooks
|
|
10
|
+
echo "Git hooks installed. Pre-commit will run verify-cascade.sh on docs changes."
|
|
11
|
+
echo ""
|
|
12
|
+
echo "Configuration:"
|
|
13
|
+
echo " Default: warn only (commits proceed despite warnings)"
|
|
14
|
+
echo " Strict: copy .cascaderc.example to .cascaderc and set CASCADE_STRICT=1"
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# sync-metrics.sh — Propagates metrics.yml values to all docs that reference them.
|
|
3
|
+
# Finds [**N**](metrics.yml#field) links and updates N to match metrics.yml.
|
|
4
|
+
# Also handles legacy format: **N** (→ metrics.yml#field)
|
|
5
|
+
# Usage: tools/sync-metrics.sh
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
10
|
+
METRICS_FILE="$REPO_ROOT/metrics.yml"
|
|
11
|
+
DOCS_DIR="$REPO_ROOT/docs"
|
|
12
|
+
|
|
13
|
+
if [ ! -f "$METRICS_FILE" ]; then
|
|
14
|
+
echo "ERROR: metrics.yml not found at $METRICS_FILE"
|
|
15
|
+
exit 1
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
UPDATED=0
|
|
19
|
+
FILES_CHANGED=0
|
|
20
|
+
|
|
21
|
+
# Parse metrics.yml into key=value pairs (skip comments, blank lines, string values)
|
|
22
|
+
while IFS= read -r line; do
|
|
23
|
+
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
|
24
|
+
[[ -z "$line" ]] && continue
|
|
25
|
+
[[ "$line" =~ \" ]] && continue
|
|
26
|
+
|
|
27
|
+
key=$(echo "$line" | cut -d: -f1 | tr -d ' ')
|
|
28
|
+
value=$(echo "$line" | cut -d: -f2 | tr -d ' ')
|
|
29
|
+
|
|
30
|
+
[[ "$key" == "last_updated" ]] && continue
|
|
31
|
+
|
|
32
|
+
# Find docs with references to this metric
|
|
33
|
+
pattern="metrics\.yml#${key}"
|
|
34
|
+
while IFS= read -r match_file; do
|
|
35
|
+
[ -z "$match_file" ] && continue
|
|
36
|
+
|
|
37
|
+
changed=false
|
|
38
|
+
|
|
39
|
+
# Link format: [**N**](metrics.yml#field) → [**value**](metrics.yml#field)
|
|
40
|
+
if grep -q "\[\*\*[0-9]*\*\*\](metrics\.yml#${key})" "$match_file"; then
|
|
41
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
42
|
+
sed -i '' "s/\[\*\*[0-9]*\*\*\](metrics\.yml#${key})/[\*\*${value}\*\*](metrics.yml#${key})/g" "$match_file"
|
|
43
|
+
else
|
|
44
|
+
sed -i "s/\[\*\*[0-9]*\*\*\](metrics\.yml#${key})/[\*\*${value}\*\*](metrics.yml#${key})/g" "$match_file"
|
|
45
|
+
fi
|
|
46
|
+
changed=true
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# Link format without bold: [N](metrics.yml#field) → [value](metrics.yml#field)
|
|
50
|
+
if grep -q "\[[0-9]*\](metrics\.yml#${key})" "$match_file"; then
|
|
51
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
52
|
+
sed -i '' "s/\[[0-9]*\](metrics\.yml#${key})/[${value}](metrics.yml#${key})/g" "$match_file"
|
|
53
|
+
else
|
|
54
|
+
sed -i "s/\[[0-9]*\](metrics\.yml#${key})/[${value}](metrics.yml#${key})/g" "$match_file"
|
|
55
|
+
fi
|
|
56
|
+
changed=true
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# Legacy format: **N** (→ metrics.yml#field) → **value** (→ metrics.yml#field)
|
|
60
|
+
if grep -q "\*\*[0-9]*\*\* (→ metrics\.yml#${key})" "$match_file"; then
|
|
61
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
62
|
+
sed -i '' "s/\*\*[0-9]*\*\* (→ metrics\.yml#${key})/\*\*${value}\*\* (→ metrics.yml#${key})/g" "$match_file"
|
|
63
|
+
else
|
|
64
|
+
sed -i "s/\*\*[0-9]*\*\* (→ metrics\.yml#${key})/\*\*${value}\*\* (→ metrics.yml#${key})/g" "$match_file"
|
|
65
|
+
fi
|
|
66
|
+
changed=true
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
if [ "$changed" = true ]; then
|
|
70
|
+
UPDATED=$((UPDATED + 1))
|
|
71
|
+
echo "Synced: $key=$value in $match_file"
|
|
72
|
+
fi
|
|
73
|
+
done < <(grep -rl "$pattern" "$DOCS_DIR" 2>/dev/null | grep -v '/examples/' || true)
|
|
74
|
+
done < "$METRICS_FILE"
|
|
75
|
+
|
|
76
|
+
echo ""
|
|
77
|
+
echo "=== Metrics Sync ==="
|
|
78
|
+
echo "References updated: $UPDATED"
|
|
79
|
+
|
|
80
|
+
if [ $UPDATED -gt 0 ]; then
|
|
81
|
+
echo "Run tools/touch-date.sh on modified files to update their dates."
|
|
82
|
+
fi
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# touch-date.sh — Updates the "Last Updated:" line in a markdown file to today's date.
|
|
3
|
+
# Usage: tools/touch-date.sh <file> [<file2> ...]
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
TODAY=$(date +%Y-%m-%d)
|
|
8
|
+
|
|
9
|
+
if [ $# -eq 0 ]; then
|
|
10
|
+
echo "Usage: tools/touch-date.sh <file> [<file2> ...]"
|
|
11
|
+
echo "Updates the 'Last Updated:' line to today's date ($TODAY)."
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
for file in "$@"; do
|
|
16
|
+
if [ ! -f "$file" ]; then
|
|
17
|
+
echo "WARNING: File not found: $file"
|
|
18
|
+
continue
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
if grep -q '^\*\*Last Updated:\*\*' "$file"; then
|
|
22
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
23
|
+
sed -i '' "s/^\*\*Last Updated:\*\*.*/\*\*Last Updated:\*\* $TODAY/" "$file"
|
|
24
|
+
else
|
|
25
|
+
sed -i "s/^\*\*Last Updated:\*\*.*/\*\*Last Updated:\*\* $TODAY/" "$file"
|
|
26
|
+
fi
|
|
27
|
+
echo "Updated: $file → $TODAY"
|
|
28
|
+
else
|
|
29
|
+
echo "WARNING: No 'Last Updated:' line found in $file"
|
|
30
|
+
fi
|
|
31
|
+
done
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# verify-cascade.sh — Full verification of the Battle Plan cascade system.
|
|
3
|
+
# Checks: dates, metrics, staleness, battle plan freshness.
|
|
4
|
+
# Usage: tools/verify-cascade.sh
|
|
5
|
+
# Exit 0 if clean, exit 1 if issues found.
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
10
|
+
DOCS_DIR="$REPO_ROOT/docs"
|
|
11
|
+
METRICS_FILE="$REPO_ROOT/metrics.yml"
|
|
12
|
+
TODAY=$(date +%Y-%m-%d)
|
|
13
|
+
|
|
14
|
+
WARNINGS=0
|
|
15
|
+
ERRORS=0
|
|
16
|
+
|
|
17
|
+
echo "=== Battle Plan Verification ==="
|
|
18
|
+
echo "Date: $TODAY"
|
|
19
|
+
echo ""
|
|
20
|
+
|
|
21
|
+
# --- Check 1: Last Updated dates ---
|
|
22
|
+
echo "--- Check 1: Last Updated Dates ---"
|
|
23
|
+
|
|
24
|
+
while IFS= read -r doc; do
|
|
25
|
+
[ -z "$doc" ] && continue
|
|
26
|
+
|
|
27
|
+
# Skip docs/README.md (vault rules, not a cascade doc)
|
|
28
|
+
[[ "$doc" == "$DOCS_DIR/README.md" ]] && continue
|
|
29
|
+
|
|
30
|
+
status=$(grep '^\*\*Status:\*\*' "$doc" | head -1 | sed 's/\*\*Status:\*\* //' || true)
|
|
31
|
+
|
|
32
|
+
# Skip archived and draft docs
|
|
33
|
+
[[ "$status" == "Archived" ]] && continue
|
|
34
|
+
|
|
35
|
+
last_updated=$(grep '^\*\*Last Updated:\*\*' "$doc" | head -1 | sed 's/\*\*Last Updated:\*\* //' || true)
|
|
36
|
+
|
|
37
|
+
if [ -z "$last_updated" ]; then
|
|
38
|
+
echo "WARNING: No Last Updated line in $doc"
|
|
39
|
+
WARNINGS=$((WARNINGS + 1))
|
|
40
|
+
fi
|
|
41
|
+
done < <(find "$DOCS_DIR" -name "*.md" -not -path "*/examples/*" 2>/dev/null)
|
|
42
|
+
|
|
43
|
+
# --- Check 2: Metrics consistency ---
|
|
44
|
+
echo ""
|
|
45
|
+
echo "--- Check 2: Metrics Consistency ---"
|
|
46
|
+
|
|
47
|
+
if [ -f "$METRICS_FILE" ]; then
|
|
48
|
+
if ! "$REPO_ROOT/tools/check-metrics.sh" 2>&1; then
|
|
49
|
+
ERRORS=$((ERRORS + 1))
|
|
50
|
+
fi
|
|
51
|
+
else
|
|
52
|
+
echo "WARNING: metrics.yml not found"
|
|
53
|
+
WARNINGS=$((WARNINGS + 1))
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# --- Check 3: Battle plan freshness ---
|
|
57
|
+
echo ""
|
|
58
|
+
echo "--- Check 3: Battle Plan Freshness ---"
|
|
59
|
+
|
|
60
|
+
BATTLE_PLAN="$DOCS_DIR/battle-plan.md"
|
|
61
|
+
if [ -f "$BATTLE_PLAN" ]; then
|
|
62
|
+
bp_date=$(grep '^\*\*Last Updated:\*\*' "$BATTLE_PLAN" | head -1 | sed 's/\*\*Last Updated:\*\* //' || true)
|
|
63
|
+
|
|
64
|
+
while IFS= read -r doc; do
|
|
65
|
+
[ -z "$doc" ] && continue
|
|
66
|
+
[[ "$doc" == "$BATTLE_PLAN" ]] && continue
|
|
67
|
+
[[ "$doc" == "$DOCS_DIR/README.md" ]] && continue
|
|
68
|
+
|
|
69
|
+
doc_date=$(grep '^\*\*Last Updated:\*\*' "$doc" | head -1 | sed 's/\*\*Last Updated:\*\* //' || true)
|
|
70
|
+
[ -z "$doc_date" ] && continue
|
|
71
|
+
|
|
72
|
+
if [[ "$doc_date" > "$bp_date" ]]; then
|
|
73
|
+
echo "WARNING: $doc ($doc_date) is newer than battle plan ($bp_date)"
|
|
74
|
+
WARNINGS=$((WARNINGS + 1))
|
|
75
|
+
fi
|
|
76
|
+
done < <(find "$DOCS_DIR" -name "*.md" -not -path "*/examples/*" 2>/dev/null)
|
|
77
|
+
else
|
|
78
|
+
echo "WARNING: Battle plan not found at $BATTLE_PLAN"
|
|
79
|
+
WARNINGS=$((WARNINGS + 1))
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
# --- Check 4: TL;DR existence ---
|
|
83
|
+
echo ""
|
|
84
|
+
echo "--- Check 4: TL;DR Presence ---"
|
|
85
|
+
|
|
86
|
+
while IFS= read -r doc; do
|
|
87
|
+
[ -z "$doc" ] && continue
|
|
88
|
+
|
|
89
|
+
# Skip docs/README.md (vault rules, not a cascade doc)
|
|
90
|
+
[[ "$doc" == "$DOCS_DIR/README.md" ]] && continue
|
|
91
|
+
|
|
92
|
+
status=$(grep '^\*\*Status:\*\*' "$doc" | head -1 | sed 's/\*\*Status:\*\* //' || true)
|
|
93
|
+
[[ "$status" == "Archived" ]] && continue
|
|
94
|
+
|
|
95
|
+
if ! grep -q '^\*\*TL;DR:\*\*' "$doc"; then
|
|
96
|
+
echo "WARNING: No TL;DR in $doc"
|
|
97
|
+
WARNINGS=$((WARNINGS + 1))
|
|
98
|
+
fi
|
|
99
|
+
done < <(find "$DOCS_DIR" -name "*.md" -not -path "*/examples/*" 2>/dev/null)
|
|
100
|
+
|
|
101
|
+
# --- Check 5: Stale inline references ---
|
|
102
|
+
echo ""
|
|
103
|
+
echo "--- Check 5: Inline Reference Staleness ---"
|
|
104
|
+
|
|
105
|
+
while IFS= read -r doc; do
|
|
106
|
+
[ -z "$doc" ] && continue
|
|
107
|
+
|
|
108
|
+
# Skip docs/README.md (vault rules, contains example syntax)
|
|
109
|
+
[[ "$doc" == "$DOCS_DIR/README.md" ]] && continue
|
|
110
|
+
|
|
111
|
+
doc_date=$(grep '^\*\*Last Updated:\*\*' "$doc" | head -1 | sed 's/\*\*Last Updated:\*\* //' || true)
|
|
112
|
+
[ -z "$doc_date" ] && continue
|
|
113
|
+
|
|
114
|
+
while IFS= read -r ref; do
|
|
115
|
+
[ -z "$ref" ] && continue
|
|
116
|
+
ref_file=$(echo "$ref" | grep -oE '[a-zA-Z0-9_-]+\.md' || true)
|
|
117
|
+
[ -z "$ref_file" ] && continue
|
|
118
|
+
|
|
119
|
+
# Skip metrics.yml references (handled by check-metrics)
|
|
120
|
+
[[ "$ref" == *"metrics.yml"* ]] && continue
|
|
121
|
+
|
|
122
|
+
ref_path=$(find "$DOCS_DIR" -name "$ref_file" -not -path "*/examples/*" 2>/dev/null | head -1)
|
|
123
|
+
if [ -z "$ref_path" ]; then
|
|
124
|
+
echo "WARNING: Referenced file $ref_file not found (from $doc)"
|
|
125
|
+
WARNINGS=$((WARNINGS + 1))
|
|
126
|
+
continue
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
ref_date=$(grep '^\*\*Last Updated:\*\*' "$ref_path" | head -1 | sed 's/\*\*Last Updated:\*\* //' || true)
|
|
130
|
+
[ -z "$ref_date" ] && continue
|
|
131
|
+
|
|
132
|
+
if [[ "$ref_date" > "$doc_date" ]]; then
|
|
133
|
+
echo "WARNING: $doc references $ref_file, but $ref_file ($ref_date) is newer than $doc ($doc_date) — reference may be stale"
|
|
134
|
+
WARNINGS=$((WARNINGS + 1))
|
|
135
|
+
fi
|
|
136
|
+
done < <(grep -oE '\(→ [^)]+\)' "$doc" 2>/dev/null || true)
|
|
137
|
+
done < <(find "$DOCS_DIR" -name "*.md" -not -path "*/examples/*" 2>/dev/null)
|
|
138
|
+
|
|
139
|
+
# --- Summary ---
|
|
140
|
+
echo ""
|
|
141
|
+
echo "========================="
|
|
142
|
+
echo "Warnings: $WARNINGS"
|
|
143
|
+
echo "Errors: $ERRORS"
|
|
144
|
+
|
|
145
|
+
if [ $ERRORS -gt 0 ]; then
|
|
146
|
+
echo "RESULT: FAIL"
|
|
147
|
+
exit 1
|
|
148
|
+
elif [ $WARNINGS -gt 0 ]; then
|
|
149
|
+
echo "RESULT: PASS with warnings"
|
|
150
|
+
exit 0
|
|
151
|
+
else
|
|
152
|
+
echo "RESULT: CLEAN"
|
|
153
|
+
exit 0
|
|
154
|
+
fi
|