mindsystem-cc 3.17.1 → 3.18.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/agents/ms-debugger.md +3 -3
- package/agents/ms-researcher.md +13 -13
- package/commands/ms/complete-milestone.md +47 -54
- package/mindsystem/references/routing/next-phase-routing.md +1 -1
- package/mindsystem/templates/milestone-archive.md +3 -3
- package/mindsystem/templates/research-subagent-prompt.md +2 -2
- package/mindsystem/templates/research.md +7 -7
- package/mindsystem/workflows/complete-milestone.md +52 -227
- package/package.json +1 -1
- package/scripts/archive-milestone-files.sh +68 -0
- package/scripts/archive-milestone-phases.sh +138 -0
- package/scripts/gather-milestone-stats.sh +179 -0
- package/scripts/ms-lookup/ms_lookup/backends/context7.py +17 -5
- package/scripts/ms-lookup/ms_lookup/backends/perplexity.py +17 -3
- package/scripts/ms-lookup-wrapper.sh +1 -1
- package/scripts/validate-execution-order.sh +4 -5
- package/scripts/cleanup-phase-artifacts.sh +0 -68
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# gather-milestone-stats.sh
|
|
4
|
+
# Gathers milestone readiness status and statistics from the filesystem
|
|
5
|
+
# and git history. Outputs structured text for the LLM to present.
|
|
6
|
+
#
|
|
7
|
+
# Usage: ./scripts/gather-milestone-stats.sh <start_phase> <end_phase>
|
|
8
|
+
# Example: ./scripts/gather-milestone-stats.sh 1 6
|
|
9
|
+
|
|
10
|
+
set -e
|
|
11
|
+
|
|
12
|
+
# --- Validation ---
|
|
13
|
+
if [ -z "$1" ] || [ -z "$2" ]; then
|
|
14
|
+
echo "Error: Two arguments required"
|
|
15
|
+
echo "Usage: $0 <start_phase> <end_phase>"
|
|
16
|
+
exit 1
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
START="$1"
|
|
20
|
+
END="$2"
|
|
21
|
+
|
|
22
|
+
if ! [[ "$START" =~ ^[0-9]+$ ]] || ! [[ "$END" =~ ^[0-9]+$ ]]; then
|
|
23
|
+
echo "Error: Both arguments must be numeric"
|
|
24
|
+
echo "Usage: $0 <start_phase> <end_phase>"
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
if [ "$START" -gt "$END" ]; then
|
|
29
|
+
echo "Error: Start phase ($START) cannot exceed end phase ($END)"
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# --- Find .planning from git root ---
|
|
34
|
+
GIT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
|
|
35
|
+
if [ -z "$GIT_ROOT" ]; then
|
|
36
|
+
echo "Error: Not in a git repository"
|
|
37
|
+
exit 1
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
PHASES_DIR="$GIT_ROOT/.planning/phases"
|
|
41
|
+
if [ ! -d "$PHASES_DIR" ]; then
|
|
42
|
+
echo "Error: Phases directory not found at $PHASES_DIR"
|
|
43
|
+
exit 1
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# --- Helper: check if phase number is in range (supports decimals like 02.1) ---
|
|
47
|
+
in_range() {
|
|
48
|
+
local phase_num="$1"
|
|
49
|
+
echo "$phase_num" | awk -v s="$START" -v e="$END" '{
|
|
50
|
+
val = $1 + 0
|
|
51
|
+
if (val >= s && val <= e + 0.999) exit 0
|
|
52
|
+
else exit 1
|
|
53
|
+
}'
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# ============================================================
|
|
57
|
+
# READINESS
|
|
58
|
+
# ============================================================
|
|
59
|
+
echo "=== Readiness ==="
|
|
60
|
+
echo ""
|
|
61
|
+
|
|
62
|
+
PHASE_COUNT=0
|
|
63
|
+
PLAN_COUNT=0
|
|
64
|
+
COMPLETE=0
|
|
65
|
+
INCOMPLETE_LIST=""
|
|
66
|
+
PHASE_DETAILS=""
|
|
67
|
+
|
|
68
|
+
for dir in "$PHASES_DIR"/*/; do
|
|
69
|
+
[ -d "$dir" ] || continue
|
|
70
|
+
dirname=$(basename "$dir")
|
|
71
|
+
phase_num="${dirname%%-*}"
|
|
72
|
+
phase_name="${dirname#*-}"
|
|
73
|
+
|
|
74
|
+
if in_range "$phase_num"; then
|
|
75
|
+
PHASE_COUNT=$((PHASE_COUNT + 1))
|
|
76
|
+
phase_plans=0
|
|
77
|
+
phase_complete=0
|
|
78
|
+
|
|
79
|
+
for plan in "$dir"/*-PLAN.md; do
|
|
80
|
+
[ -f "$plan" ] || continue
|
|
81
|
+
PLAN_COUNT=$((PLAN_COUNT + 1))
|
|
82
|
+
phase_plans=$((phase_plans + 1))
|
|
83
|
+
plan_base=$(basename "$plan" -PLAN.md)
|
|
84
|
+
summary="$dir/${plan_base}-SUMMARY.md"
|
|
85
|
+
if [ -f "$summary" ]; then
|
|
86
|
+
COMPLETE=$((COMPLETE + 1))
|
|
87
|
+
phase_complete=$((phase_complete + 1))
|
|
88
|
+
else
|
|
89
|
+
INCOMPLETE_LIST+=" $(basename "$dir")/$(basename "$plan")"$'\n'
|
|
90
|
+
fi
|
|
91
|
+
done
|
|
92
|
+
|
|
93
|
+
PHASE_DETAILS+="- Phase $phase_num: $phase_name ($phase_complete/$phase_plans plans)"$'\n'
|
|
94
|
+
fi
|
|
95
|
+
done
|
|
96
|
+
|
|
97
|
+
echo "Phases: $PHASE_COUNT (range $START-$END)"
|
|
98
|
+
echo "Plans: $PLAN_COUNT total, $COMPLETE complete"
|
|
99
|
+
echo ""
|
|
100
|
+
echo "$PHASE_DETAILS"
|
|
101
|
+
|
|
102
|
+
if [ "$COMPLETE" -eq "$PLAN_COUNT" ] && [ "$PLAN_COUNT" -gt 0 ]; then
|
|
103
|
+
echo "Status: READY"
|
|
104
|
+
else
|
|
105
|
+
INCOMPLETE=$((PLAN_COUNT - COMPLETE))
|
|
106
|
+
echo "Incomplete ($INCOMPLETE):"
|
|
107
|
+
echo "$INCOMPLETE_LIST"
|
|
108
|
+
echo "Status: NOT READY"
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
# ============================================================
|
|
112
|
+
# GIT STATS
|
|
113
|
+
# ============================================================
|
|
114
|
+
echo ""
|
|
115
|
+
echo "=== Git Stats ==="
|
|
116
|
+
echo ""
|
|
117
|
+
|
|
118
|
+
# Collect commits matching Mindsystem phase convention: feat(XX-YY), fix(XX-YY), etc.
|
|
119
|
+
ALL_COMMITS=""
|
|
120
|
+
for i in $(seq "$START" "$END"); do
|
|
121
|
+
phase=$(printf "%02d" "$i")
|
|
122
|
+
commits=$(git log --all --format="%H %ai %s" --grep="($phase-" 2>/dev/null || true)
|
|
123
|
+
if [ -n "$commits" ]; then
|
|
124
|
+
ALL_COMMITS+="$commits"$'\n'
|
|
125
|
+
fi
|
|
126
|
+
done
|
|
127
|
+
|
|
128
|
+
# Also capture decimal phase commits (e.g., 02.1 inserted phases)
|
|
129
|
+
for dir in "$PHASES_DIR"/*/; do
|
|
130
|
+
[ -d "$dir" ] || continue
|
|
131
|
+
dirname=$(basename "$dir")
|
|
132
|
+
phase_num="${dirname%%-*}"
|
|
133
|
+
case "$phase_num" in
|
|
134
|
+
*.*) # Decimal phase — not captured by seq
|
|
135
|
+
if in_range "$phase_num"; then
|
|
136
|
+
commits=$(git log --all --format="%H %ai %s" --grep="($phase_num-" 2>/dev/null || true)
|
|
137
|
+
if [ -n "$commits" ]; then
|
|
138
|
+
ALL_COMMITS+="$commits"$'\n'
|
|
139
|
+
fi
|
|
140
|
+
fi
|
|
141
|
+
;;
|
|
142
|
+
esac
|
|
143
|
+
done
|
|
144
|
+
|
|
145
|
+
# Remove empty lines, deduplicate, sort by date
|
|
146
|
+
ALL_COMMITS=$(echo "$ALL_COMMITS" | grep -v '^$' | sort -u -k2,3)
|
|
147
|
+
|
|
148
|
+
if [ -n "$ALL_COMMITS" ]; then
|
|
149
|
+
COMMIT_COUNT=$(echo "$ALL_COMMITS" | wc -l | tr -d ' ')
|
|
150
|
+
FIRST_LINE=$(echo "$ALL_COMMITS" | head -1)
|
|
151
|
+
LAST_LINE=$(echo "$ALL_COMMITS" | tail -1)
|
|
152
|
+
FIRST_HASH=$(echo "$FIRST_LINE" | awk '{print $1}')
|
|
153
|
+
LAST_HASH=$(echo "$LAST_LINE" | awk '{print $1}')
|
|
154
|
+
FIRST_DATE=$(echo "$FIRST_LINE" | awk '{print $2}')
|
|
155
|
+
LAST_DATE=$(echo "$LAST_LINE" | awk '{print $2}')
|
|
156
|
+
FIRST_MSG=$(echo "$FIRST_LINE" | cut -d' ' -f4-)
|
|
157
|
+
LAST_MSG=$(echo "$LAST_LINE" | cut -d' ' -f4-)
|
|
158
|
+
|
|
159
|
+
# Calculate days
|
|
160
|
+
DAYS=$(python3 -c "from datetime import date; print((date.fromisoformat('$LAST_DATE') - date.fromisoformat('$FIRST_DATE')).days)" 2>/dev/null || echo "?")
|
|
161
|
+
|
|
162
|
+
echo "Commits: $COMMIT_COUNT"
|
|
163
|
+
echo "Git range: ${FIRST_HASH:0:7}..${LAST_HASH:0:7}"
|
|
164
|
+
echo "First: $FIRST_DATE — $FIRST_MSG"
|
|
165
|
+
echo "Last: $LAST_DATE — $LAST_MSG"
|
|
166
|
+
echo "Timeline: $DAYS days ($FIRST_DATE → $LAST_DATE)"
|
|
167
|
+
|
|
168
|
+
# Diff stats for the range
|
|
169
|
+
DIFFSTAT=$(git diff --shortstat "${FIRST_HASH}^..${LAST_HASH}" 2>/dev/null || true)
|
|
170
|
+
if [ -n "$DIFFSTAT" ]; then
|
|
171
|
+
echo "Changes:$DIFFSTAT"
|
|
172
|
+
fi
|
|
173
|
+
else
|
|
174
|
+
echo "No commits found matching phase patterns (expected 'feat(XX-YY): ...')"
|
|
175
|
+
echo "Determine git range manually from git log"
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
echo ""
|
|
179
|
+
exit 0
|
|
@@ -25,6 +25,16 @@ class Context7Client:
|
|
|
25
25
|
],
|
|
26
26
|
)
|
|
27
27
|
|
|
28
|
+
@staticmethod
|
|
29
|
+
def _extract_error_body(response: httpx.Response) -> str:
|
|
30
|
+
"""Extract human-readable error message from response."""
|
|
31
|
+
try:
|
|
32
|
+
data = response.json()
|
|
33
|
+
# Context7 style: {"error": "code", "message": "..."}
|
|
34
|
+
return data.get("message", data.get("error", response.text[:200]))
|
|
35
|
+
except Exception:
|
|
36
|
+
return response.text[:200]
|
|
37
|
+
|
|
28
38
|
def resolve_library(self, library_name: str, query: str) -> dict:
|
|
29
39
|
"""Resolve library name to Context7 library ID.
|
|
30
40
|
|
|
@@ -50,21 +60,22 @@ class Context7Client:
|
|
|
50
60
|
response.raise_for_status()
|
|
51
61
|
data = response.json()
|
|
52
62
|
except httpx.HTTPStatusError as e:
|
|
63
|
+
body = self._extract_error_body(e.response)
|
|
53
64
|
if e.response.status_code == 401:
|
|
54
65
|
raise MsLookupError(
|
|
55
66
|
ErrorCode.MISSING_API_KEY,
|
|
56
|
-
"Invalid CONTEXT7_API_KEY",
|
|
67
|
+
f"Invalid CONTEXT7_API_KEY — {body}",
|
|
57
68
|
suggestions=["Check your API key at https://context7.com/dashboard"],
|
|
58
69
|
)
|
|
59
70
|
elif e.response.status_code == 429:
|
|
60
71
|
raise MsLookupError(
|
|
61
72
|
ErrorCode.RATE_LIMITED,
|
|
62
|
-
"Context7 API rate
|
|
73
|
+
f"Context7 API rate limited — {body}",
|
|
63
74
|
suggestions=["Wait a moment and try again", "Use --no-cache sparingly"],
|
|
64
75
|
)
|
|
65
76
|
raise MsLookupError(
|
|
66
77
|
ErrorCode.API_ERROR,
|
|
67
|
-
f"Context7 API error
|
|
78
|
+
f"Context7 API error ({e.response.status_code}): {body}",
|
|
68
79
|
)
|
|
69
80
|
except httpx.RequestError as e:
|
|
70
81
|
raise MsLookupError(
|
|
@@ -122,6 +133,7 @@ class Context7Client:
|
|
|
122
133
|
# Plain text/markdown response - wrap in a structure
|
|
123
134
|
data = {"content": response.text, "format": "markdown"}
|
|
124
135
|
except httpx.HTTPStatusError as e:
|
|
136
|
+
body = self._extract_error_body(e.response)
|
|
125
137
|
if e.response.status_code == 404:
|
|
126
138
|
raise MsLookupError(
|
|
127
139
|
ErrorCode.LIBRARY_NOT_FOUND,
|
|
@@ -130,11 +142,11 @@ class Context7Client:
|
|
|
130
142
|
elif e.response.status_code == 429:
|
|
131
143
|
raise MsLookupError(
|
|
132
144
|
ErrorCode.RATE_LIMITED,
|
|
133
|
-
"Context7 API rate
|
|
145
|
+
f"Context7 API rate limited — {body}",
|
|
134
146
|
)
|
|
135
147
|
raise MsLookupError(
|
|
136
148
|
ErrorCode.API_ERROR,
|
|
137
|
-
f"Context7 API error
|
|
149
|
+
f"Context7 API error ({e.response.status_code}): {body}",
|
|
138
150
|
)
|
|
139
151
|
except httpx.RequestError as e:
|
|
140
152
|
raise MsLookupError(
|
|
@@ -81,21 +81,22 @@ class PerplexityClient:
|
|
|
81
81
|
response.raise_for_status()
|
|
82
82
|
data = response.json()
|
|
83
83
|
except httpx.HTTPStatusError as e:
|
|
84
|
+
body = self._extract_error_body(e.response)
|
|
84
85
|
if e.response.status_code == 401:
|
|
85
86
|
raise MsLookupError(
|
|
86
87
|
ErrorCode.MISSING_API_KEY,
|
|
87
|
-
"Invalid PERPLEXITY_API_KEY",
|
|
88
|
+
f"Invalid PERPLEXITY_API_KEY — {body}",
|
|
88
89
|
suggestions=["Check your API key at https://docs.perplexity.ai/"],
|
|
89
90
|
)
|
|
90
91
|
elif e.response.status_code == 429:
|
|
91
92
|
raise MsLookupError(
|
|
92
93
|
ErrorCode.RATE_LIMITED,
|
|
93
|
-
"Perplexity API rate
|
|
94
|
+
f"Perplexity API rate limited — {body}",
|
|
94
95
|
suggestions=["Wait a moment and try again"],
|
|
95
96
|
)
|
|
96
97
|
raise MsLookupError(
|
|
97
98
|
ErrorCode.API_ERROR,
|
|
98
|
-
f"Perplexity API error
|
|
99
|
+
f"Perplexity API error ({e.response.status_code}): {body}",
|
|
99
100
|
)
|
|
100
101
|
except httpx.RequestError as e:
|
|
101
102
|
raise MsLookupError(
|
|
@@ -105,6 +106,19 @@ class PerplexityClient:
|
|
|
105
106
|
|
|
106
107
|
return data
|
|
107
108
|
|
|
109
|
+
@staticmethod
|
|
110
|
+
def _extract_error_body(response: httpx.Response) -> str:
|
|
111
|
+
"""Extract human-readable error message from response."""
|
|
112
|
+
try:
|
|
113
|
+
data = response.json()
|
|
114
|
+
# OpenAI-style: {"error": {"message": "..."}}
|
|
115
|
+
if isinstance(data.get("error"), dict):
|
|
116
|
+
return data["error"].get("message", str(data["error"]))
|
|
117
|
+
# Simple: {"error": "...", "message": "..."}
|
|
118
|
+
return data.get("message", data.get("error", response.text[:200]))
|
|
119
|
+
except Exception:
|
|
120
|
+
return response.text[:200]
|
|
121
|
+
|
|
108
122
|
def _strip_think_tags(self, content: str) -> str:
|
|
109
123
|
"""Remove <think>...</think> blocks from response."""
|
|
110
124
|
# Remove think tags and their content (including multiline)
|
|
@@ -10,7 +10,7 @@ fi
|
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
cd "$SCRIPT_DIR/ms-lookup"
|
|
12
12
|
|
|
13
|
-
if command -v uv
|
|
13
|
+
if command -v uv > /dev/null 2>&1; then
|
|
14
14
|
# Prefer uv if available (faster)
|
|
15
15
|
uv sync --quiet 2>/dev/null
|
|
16
16
|
uv run python -m ms_lookup "$@"
|
|
@@ -70,13 +70,13 @@ fi
|
|
|
70
70
|
# --- Check 3 (warning): File conflicts within waves ---
|
|
71
71
|
CURRENT_WAVE=""
|
|
72
72
|
WAVE_COUNT=0
|
|
73
|
-
|
|
73
|
+
CURRENT_WAVE_FILES=""
|
|
74
74
|
|
|
75
75
|
while IFS= read -r line; do
|
|
76
76
|
if echo "$line" | grep -qE '^## Wave [0-9]+'; then
|
|
77
77
|
CURRENT_WAVE=$(echo "$line" | grep -oE '[0-9]+')
|
|
78
78
|
WAVE_COUNT=$((WAVE_COUNT + 1))
|
|
79
|
-
|
|
79
|
+
CURRENT_WAVE_FILES=""
|
|
80
80
|
elif [ -n "$CURRENT_WAVE" ]; then
|
|
81
81
|
PLAN_FILE=$(echo "$line" | grep -oE '[0-9][0-9.]*-[0-9]+-PLAN\.md' || true)
|
|
82
82
|
if [ -n "$PLAN_FILE" ] && [ -f "$PHASE_DIR/$PLAN_FILE" ]; then
|
|
@@ -84,11 +84,10 @@ while IFS= read -r line; do
|
|
|
84
84
|
FILE_PATHS=$(grep -E '^\*\*Files:\*\*' "$PHASE_DIR/$PLAN_FILE" | sed 's/\*\*Files:\*\*//g' | tr ',' '\n' | sed 's/`//g; s/^[[:space:]]*//; s/[[:space:]]*$//' | grep -v '^$' || true)
|
|
85
85
|
while IFS= read -r fpath; do
|
|
86
86
|
[ -z "$fpath" ] && continue
|
|
87
|
-
|
|
88
|
-
if echo "$EXISTING" | grep -qF "|$fpath|"; then
|
|
87
|
+
if echo "$CURRENT_WAVE_FILES" | grep -qF "|$fpath|"; then
|
|
89
88
|
echo "WARNING: File '$fpath' appears in multiple plans within Wave $CURRENT_WAVE"
|
|
90
89
|
else
|
|
91
|
-
|
|
90
|
+
CURRENT_WAVE_FILES="${CURRENT_WAVE_FILES}|$fpath|"
|
|
92
91
|
fi
|
|
93
92
|
done <<< "$FILE_PATHS"
|
|
94
93
|
fi
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
#
|
|
3
|
-
# cleanup-phase-artifacts.sh
|
|
4
|
-
# Deletes raw phase artifacts (CONTEXT, DESIGN, RESEARCH, SUMMARY, UAT,
|
|
5
|
-
# VERIFICATION, EXECUTION-ORDER) from all phases in a milestone range.
|
|
6
|
-
# Knowledge files in .planning/knowledge/ are not touched.
|
|
7
|
-
#
|
|
8
|
-
# Usage: ./scripts/cleanup-phase-artifacts.sh <start_phase> <end_phase>
|
|
9
|
-
# Example: ./scripts/cleanup-phase-artifacts.sh 1 6
|
|
10
|
-
|
|
11
|
-
set -e
|
|
12
|
-
|
|
13
|
-
# --- Validation ---
|
|
14
|
-
if [ -z "$1" ] || [ -z "$2" ]; then
|
|
15
|
-
echo "Error: Two arguments required"
|
|
16
|
-
echo "Usage: $0 <start_phase> <end_phase>"
|
|
17
|
-
exit 1
|
|
18
|
-
fi
|
|
19
|
-
|
|
20
|
-
START="$1"
|
|
21
|
-
END="$2"
|
|
22
|
-
|
|
23
|
-
if ! [[ "$START" =~ ^[0-9]+$ ]] || ! [[ "$END" =~ ^[0-9]+$ ]]; then
|
|
24
|
-
echo "Error: Both arguments must be numeric"
|
|
25
|
-
echo "Usage: $0 <start_phase> <end_phase>"
|
|
26
|
-
exit 1
|
|
27
|
-
fi
|
|
28
|
-
|
|
29
|
-
if [ "$START" -gt "$END" ]; then
|
|
30
|
-
echo "Error: Start phase ($START) cannot exceed end phase ($END)"
|
|
31
|
-
exit 1
|
|
32
|
-
fi
|
|
33
|
-
|
|
34
|
-
# --- Find .planning from git root ---
|
|
35
|
-
GIT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
|
|
36
|
-
if [ -z "$GIT_ROOT" ]; then
|
|
37
|
-
echo "Error: Not in a git repository"
|
|
38
|
-
exit 1
|
|
39
|
-
fi
|
|
40
|
-
|
|
41
|
-
PHASES_DIR="$GIT_ROOT/.planning/phases"
|
|
42
|
-
if [ ! -d "$PHASES_DIR" ]; then
|
|
43
|
-
echo "Error: Phases directory not found at $PHASES_DIR"
|
|
44
|
-
exit 1
|
|
45
|
-
fi
|
|
46
|
-
|
|
47
|
-
# --- Delete artifacts from each phase in range ---
|
|
48
|
-
DELETED=0
|
|
49
|
-
for dir in "$PHASES_DIR"/*/; do
|
|
50
|
-
[ -d "$dir" ] || continue
|
|
51
|
-
dirname=$(basename "$dir")
|
|
52
|
-
phase_num="${dirname%%-*}"
|
|
53
|
-
# Strip leading zeros for numeric comparison
|
|
54
|
-
phase_int=$((10#$phase_num))
|
|
55
|
-
if [ "$phase_int" -ge "$START" ] && [ "$phase_int" -le "$END" ]; then
|
|
56
|
-
for f in "$dir"/*-CONTEXT.md "$dir"/*-DESIGN.md "$dir"/*-RESEARCH.md \
|
|
57
|
-
"$dir"/*-SUMMARY.md "$dir"/*-UAT.md "$dir"/*-VERIFICATION.md \
|
|
58
|
-
"$dir"/*-EXECUTION-ORDER.md; do
|
|
59
|
-
if [ -f "$f" ]; then
|
|
60
|
-
rm -f "$f"
|
|
61
|
-
DELETED=$((DELETED + 1))
|
|
62
|
-
fi
|
|
63
|
-
done
|
|
64
|
-
fi
|
|
65
|
-
done
|
|
66
|
-
|
|
67
|
-
echo "Cleaned $DELETED artifact files from phases $START-$END"
|
|
68
|
-
exit 0
|