Skip to content

Commit 5dd52f3

Browse files
committed
Refactor feature specification template for clarity and conciseness; add markdownlint configuration; implement ASCII and Mermaid diagram generators in Bash and PowerShell for architecture views.
1 parent 36be19a commit 5dd52f3

27 files changed

Lines changed: 2603 additions & 224 deletions

.markdownlint.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"default": true,
3+
"MD013": false,
4+
"MD033": false,
5+
"MD041": false,
6+
"MD024": {
7+
"siblings_only": true
8+
},
9+
"MD046": false,
10+
"MD029": false
11+
}

docs/quickstart.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ This guide will help you get started with Spec-Driven Development using Agentic
1515

1616
Specify supports two workflow modes that control development complexity, plus configurable framework opinions:
1717

18-
- **`spec` mode (default)**: Full structured development with comprehensive requirements, research, and validation
19-
- **`build` mode**: Lightweight approach focused on quick implementation and exploration
18+
- **`spec` mode (default)**: Full structured development with comprehensive requirements, research, validation, and blocking review gates for team coordination
19+
- **`build` mode (GSD - Get Sh*t Done)**: High-velocity execution with atomic commits, non-blocking post-hoc review, and minimal documentation for rapid iteration
2020

2121
**Framework Opinions** (configurable within each mode):
2222

@@ -44,7 +44,11 @@ Specify supports two workflow modes that control development complexity, plus co
4444
/mode --info
4545
```
4646

47-
**Recommendation:** Start with `build` mode for exploration, switch to `spec` mode when features become complex or need thorough documentation.
47+
**Recommendation:**
48+
49+
- Use **`build` mode** for: Individual development, rapid prototyping, quick wins, senior engineer autonomy
50+
- Use **`spec` mode** for: Team collaboration, complex systems, comprehensive documentation, rigorous validation
51+
- Switch between modes as needed: `/mode build` for fast iteration, `/mode spec` for thorough planning
4852

4953
1. **Project Initialization (`/init`)**
5054
**Action:** From the project root, run the Agentic SDLC Spec Kit `init` command (e.g., `specify init <project> --team-ai-directives https://github.com/your-org/team-ai-directives.git`) to configure local settings and clone the shared `team-ai-directives` modules.

roadmap.md

Lines changed: 131 additions & 44 deletions
Large diffs are not rendered by default.

scripts/bash/ascii-generator.sh

Lines changed: 329 additions & 0 deletions
Large diffs are not rendered by default.

scripts/bash/check-prerequisites.sh

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,15 +202,17 @@ check_feature_branch "$CURRENT_BRANCH" "$HAS_GIT" || exit 1
202202
if $PATHS_ONLY; then
203203
if $JSON_MODE; then
204204
# Minimal JSON paths payload (no validation performed)
205-
printf '{"REPO_ROOT":"%s","BRANCH":"%s","FEATURE_DIR":"%s","FEATURE_SPEC":"%s","IMPL_PLAN":"%s","TASKS":"%s"}\n' \
206-
"$REPO_ROOT" "$CURRENT_BRANCH" "$FEATURE_DIR" "$FEATURE_SPEC" "$IMPL_PLAN" "$TASKS"
205+
printf '{"REPO_ROOT":"%s","BRANCH":"%s","FEATURE_DIR":"%s","FEATURE_SPEC":"%s","IMPL_PLAN":"%s","TASKS":"%s","CONSTITUTION":"%s","ARCHITECTURE":"%s"}\n' \
206+
"$REPO_ROOT" "$CURRENT_BRANCH" "$FEATURE_DIR" "$FEATURE_SPEC" "$IMPL_PLAN" "$TASKS" "$CONSTITUTION" "$ARCHITECTURE"
207207
else
208208
echo "REPO_ROOT: $REPO_ROOT"
209209
echo "BRANCH: $CURRENT_BRANCH"
210210
echo "FEATURE_DIR: $FEATURE_DIR"
211211
echo "FEATURE_SPEC: $FEATURE_SPEC"
212212
echo "IMPL_PLAN: $IMPL_PLAN"
213213
echo "TASKS: $TASKS"
214+
echo "CONSTITUTION: $CONSTITUTION"
215+
echo "ARCHITECTURE: $ARCHITECTURE"
214216
fi
215217
exit 0
216218
fi
@@ -298,7 +300,29 @@ if $JSON_MODE; then
298300
SPEC_RISKS=$(extract_risks "$FEATURE_SPEC")
299301
PLAN_RISKS=$(extract_risks "$IMPL_PLAN")
300302
MODE_CONFIG=$(get_mode_config)
301-
printf '{"FEATURE_DIR":"%s","AVAILABLE_DOCS":%s,"SPEC_RISKS":%s,"PLAN_RISKS":%s,"MODE_CONFIG":%s}\n' "$FEATURE_DIR" "$json_docs" "$SPEC_RISKS" "$PLAN_RISKS" "$MODE_CONFIG"
303+
304+
# Check for constitution and architecture (optional governance documents)
305+
CONSTITUTION_EXISTS="false"
306+
ARCHITECTURE_EXISTS="false"
307+
CONSTITUTION_RULES="[]"
308+
ARCHITECTURE_VIEWS="{}"
309+
ARCHITECTURE_DIAGRAMS="[]"
310+
311+
if [[ -f "$CONSTITUTION" ]]; then
312+
CONSTITUTION_EXISTS="true"
313+
CONSTITUTION_RULES=$(extract_constitution_rules "$CONSTITUTION")
314+
fi
315+
316+
if [[ -f "$ARCHITECTURE" ]]; then
317+
ARCHITECTURE_EXISTS="true"
318+
ARCHITECTURE_VIEWS=$(extract_architecture_views "$ARCHITECTURE")
319+
ARCHITECTURE_DIAGRAMS=$(extract_architecture_diagrams "$ARCHITECTURE")
320+
fi
321+
322+
printf '{"FEATURE_DIR":"%s","AVAILABLE_DOCS":%s,"SPEC_RISKS":%s,"PLAN_RISKS":%s,"MODE_CONFIG":%s,"CONSTITUTION":"%s","CONSTITUTION_EXISTS":%s,"CONSTITUTION_RULES":%s,"ARCHITECTURE":"%s","ARCHITECTURE_EXISTS":%s,"ARCHITECTURE_VIEWS":%s,"ARCHITECTURE_DIAGRAMS":%s}\n' \
323+
"$FEATURE_DIR" "$json_docs" "$SPEC_RISKS" "$PLAN_RISKS" "$MODE_CONFIG" \
324+
"$CONSTITUTION" "$CONSTITUTION_EXISTS" "$CONSTITUTION_RULES" \
325+
"$ARCHITECTURE" "$ARCHITECTURE_EXISTS" "$ARCHITECTURE_VIEWS" "$ARCHITECTURE_DIAGRAMS"
302326
else
303327
# Text output
304328
echo "FEATURE_DIR:$FEATURE_DIR"
@@ -335,4 +359,10 @@ PY
335359

336360
echo "SPEC_RISKS: $spec_risks_count"
337361
echo "PLAN_RISKS: $plan_risks_count"
362+
363+
# Show governance document status
364+
echo ""
365+
echo "GOVERNANCE DOCUMENTS:"
366+
check_file "$CONSTITUTION" "constitution.md (optional)"
367+
check_file "$ARCHITECTURE" "architecture.md (optional)"
338368
fi

scripts/bash/common.sh

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,85 @@ get_current_mode() {
3737
fi
3838
}
3939

40+
# Get a specific mode configuration value
41+
# Usage: get_mode_config "atomic_commits" → returns "true" or "false"
42+
# Usage: get_mode_config "skip_micro_review" → returns "true" or "false"
43+
get_mode_config() {
44+
local key="$1"
45+
local config_file
46+
config_file=$(get_global_config_path)
47+
48+
# Default to false if no config exists or jq not available
49+
if [[ ! -f "$config_file" ]] || ! command -v jq >/dev/null 2>&1; then
50+
echo "false"
51+
return
52+
fi
53+
54+
# Get current mode
55+
local mode
56+
mode=$(get_current_mode)
57+
58+
# Read mode-specific config value, default to false
59+
local value
60+
value=$(jq -r ".mode_defaults.${mode}.${key} // false" "$config_file" 2>/dev/null)
61+
62+
echo "$value"
63+
}
64+
65+
# Get architecture diagram format from global config (mermaid or ascii)
66+
# Defaults to "mermaid" if config doesn't exist or format is invalid
67+
get_architecture_diagram_format() {
68+
local config_file
69+
config_file=$(get_global_config_path)
70+
71+
# Default to mermaid if no config exists or jq not available
72+
if [[ ! -f "$config_file" ]] || ! command -v jq >/dev/null 2>&1; then
73+
echo "mermaid"
74+
return
75+
fi
76+
77+
# Read diagram format from config, default to mermaid
78+
local format
79+
format=$(jq -r '.architecture.diagram_format // "mermaid"' "$config_file" 2>/dev/null)
80+
81+
# Validate format (only mermaid or ascii allowed)
82+
if [[ "$format" == "mermaid" || "$format" == "ascii" ]]; then
83+
echo "$format"
84+
else
85+
echo "mermaid" # Fallback for invalid values
86+
fi
87+
}
88+
89+
# Validate Mermaid diagram syntax (lightweight regex validation)
90+
# Returns 0 if valid, 1 if invalid
91+
# Args: $1 - Mermaid code string
92+
validate_mermaid_syntax() {
93+
local mermaid_code="$1"
94+
95+
# Check if empty
96+
if [[ -z "$mermaid_code" ]]; then
97+
return 1
98+
fi
99+
100+
# Check for basic Mermaid diagram types
101+
if ! echo "$mermaid_code" | grep -qE '^(graph|flowchart|sequenceDiagram|classDiagram|stateDiagram|erDiagram|gantt|pie|journey|gitGraph|mindmap|timeline)'; then
102+
return 1
103+
fi
104+
105+
# Check for balanced brackets/parentheses (simplified)
106+
local open_brackets=$(echo "$mermaid_code" | grep -o '\[' | wc -l)
107+
local close_brackets=$(echo "$mermaid_code" | grep -o '\]' | wc -l)
108+
local open_parens=$(echo "$mermaid_code" | grep -o '(' | wc -l)
109+
local close_parens=$(echo "$mermaid_code" | grep -o ')' | wc -l)
110+
111+
if [[ $open_brackets -ne $close_brackets ]] || [[ $open_parens -ne $close_parens ]]; then
112+
return 1
113+
fi
114+
115+
# Basic syntax passed
116+
return 0
117+
}
118+
40119
load_team_directives_config() {
41120
local repo_root="$1"
42121

@@ -195,6 +274,11 @@ get_feature_paths() {
195274
# Use prefix-based lookup to support multiple branches per spec
196275
local feature_dir=$(find_feature_dir_by_prefix "$repo_root" "$current_branch")
197276

277+
# Project-level governance documents
278+
local memory_dir="$repo_root/.specify/memory"
279+
local constitution_file="$memory_dir/constitution.md"
280+
local architecture_file="$memory_dir/architecture.md"
281+
198282
cat <<EOF
199283
REPO_ROOT='$repo_root'
200284
CURRENT_BRANCH='$current_branch'
@@ -209,9 +293,179 @@ QUICKSTART='$feature_dir/quickstart.md'
209293
CONTEXT='$feature_dir/context.md'
210294
CONTRACTS_DIR='$feature_dir/contracts'
211295
TEAM_DIRECTIVES='${SPECIFY_TEAM_DIRECTIVES:-}'
296+
CONSTITUTION='$constitution_file'
297+
ARCHITECTURE='$architecture_file'
212298
EOF
213299
}
214300

215301
check_file() { [[ -f "$1" ]] && echo "$2" || echo "$2"; }
216302
check_dir() { [[ -d "$1" && -n $(ls -A "$1" 2>/dev/null) ]] && echo "$2" || echo "$2"; }
217303

304+
# Extract constitution principles and constraints
305+
# Returns JSON array of rules
306+
extract_constitution_rules() {
307+
local constitution_file="$1"
308+
309+
if [[ ! -f "$constitution_file" ]]; then
310+
echo "[]"
311+
return
312+
fi
313+
314+
python3 - "$constitution_file" <<'PY'
315+
import json
316+
import sys
317+
from pathlib import Path
318+
319+
constitution_file = Path(sys.argv[1])
320+
rules = []
321+
322+
try:
323+
content = constitution_file.read_text()
324+
325+
# Extract principles (lines starting with "- **Principle")
326+
for line in content.split('\n'):
327+
if line.strip().startswith('- **Principle') or line.strip().startswith('- **PRINCIPLE'):
328+
rules.append({
329+
'type': 'principle',
330+
'text': line.strip()
331+
})
332+
elif line.strip().startswith('- **Constraint') or line.strip().startswith('- **CONSTRAINT'):
333+
rules.append({
334+
'type': 'constraint',
335+
'text': line.strip()
336+
})
337+
elif line.strip().startswith('- **Pattern') or line.strip().startswith('- **PATTERN'):
338+
rules.append({
339+
'type': 'pattern',
340+
'text': line.strip()
341+
})
342+
343+
print(json.dumps(rules, ensure_ascii=False))
344+
except Exception as e:
345+
print('[]')
346+
PY
347+
}
348+
349+
# Extract architecture viewpoints from architecture.md
350+
# Returns JSON with view names and component counts
351+
extract_architecture_views() {
352+
local architecture_file="$1"
353+
354+
if [[ ! -f "$architecture_file" ]]; then
355+
echo "{}"
356+
return
357+
fi
358+
359+
python3 - "$architecture_file" <<'PY'
360+
import json
361+
import sys
362+
from pathlib import Path
363+
import re
364+
365+
architecture_file = Path(sys.argv[1])
366+
views = {}
367+
368+
try:
369+
content = architecture_file.read_text()
370+
371+
# Track which views are present
372+
view_patterns = {
373+
'context': r'###\s+3\.1\s+Context\s+View',
374+
'functional': r'###\s+3\.2\s+Functional\s+View',
375+
'information': r'###\s+3\.3\s+Information\s+View',
376+
'concurrency': r'###\s+3\.4\s+Concurrency\s+View',
377+
'development': r'###\s+3\.5\s+Development\s+View',
378+
'deployment': r'###\s+3\.6\s+Deployment\s+View',
379+
'operational': r'###\s+3\.7\s+Operational\s+View'
380+
}
381+
382+
for view_name, pattern in view_patterns.items():
383+
if re.search(pattern, content, re.IGNORECASE):
384+
views[view_name] = {'present': True}
385+
else:
386+
views[view_name] = {'present': False}
387+
388+
print(json.dumps(views, ensure_ascii=False))
389+
except Exception as e:
390+
print('{}')
391+
PY
392+
}
393+
394+
# Extract diagram blocks from architecture.md
395+
# Returns JSON array of diagrams with type and format
396+
extract_architecture_diagrams() {
397+
local architecture_file="$1"
398+
399+
if [[ ! -f "$architecture_file" ]]; then
400+
echo "[]"
401+
return
402+
fi
403+
404+
python3 - "$architecture_file" <<'PY'
405+
import json
406+
import sys
407+
from pathlib import Path
408+
import re
409+
410+
architecture_file = Path(sys.argv[1])
411+
diagrams = []
412+
413+
try:
414+
content = architecture_file.read_text()
415+
416+
# Find all code blocks (mermaid or text)
417+
code_block_pattern = r'```(mermaid|text)\n(.*?)\n```'
418+
419+
for match in re.finditer(code_block_pattern, content, re.DOTALL):
420+
diagram_format = match.group(1)
421+
diagram_content = match.group(2)
422+
423+
# Try to determine which view this diagram belongs to by context
424+
start_pos = match.start()
425+
preceding_text = content[:start_pos]
426+
427+
# Find the most recent view heading
428+
view_match = None
429+
for view_pattern in [
430+
r'###\s+3\.1\s+Context\s+View',
431+
r'###\s+3\.2\s+Functional\s+View',
432+
r'###\s+3\.3\s+Information\s+View',
433+
r'###\s+3\.4\s+Concurrency\s+View',
434+
r'###\s+3\.5\s+Development\s+View',
435+
r'###\s+3\.6\s+Deployment\s+View',
436+
r'###\s+3\.7\s+Operational\s+View'
437+
]:
438+
matches = list(re.finditer(view_pattern, preceding_text, re.IGNORECASE))
439+
if matches:
440+
view_match = matches[-1].group()
441+
break
442+
443+
view_name = 'unknown'
444+
if view_match:
445+
if 'Context' in view_match:
446+
view_name = 'context'
447+
elif 'Functional' in view_match:
448+
view_name = 'functional'
449+
elif 'Information' in view_match:
450+
view_name = 'information'
451+
elif 'Concurrency' in view_match:
452+
view_name = 'concurrency'
453+
elif 'Development' in view_match:
454+
view_name = 'development'
455+
elif 'Deployment' in view_match:
456+
view_name = 'deployment'
457+
elif 'Operational' in view_match:
458+
view_name = 'operational'
459+
460+
diagrams.append({
461+
'view': view_name,
462+
'format': diagram_format,
463+
'line_count': len(diagram_content.split('\n'))
464+
})
465+
466+
print(json.dumps(diagrams, ensure_ascii=False))
467+
except Exception as e:
468+
print('[]')
469+
PY
470+
}
471+

0 commit comments

Comments
 (0)