mindsystem-cc 3.17.1 → 3.18.1
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-consolidator.md +4 -4
- package/agents/ms-debugger.md +3 -3
- package/agents/ms-designer.md +33 -70
- package/agents/ms-executor.md +7 -6
- package/agents/ms-plan-writer.md +52 -26
- package/agents/ms-researcher.md +13 -13
- package/commands/ms/check-phase.md +1 -1
- package/commands/ms/complete-milestone.md +47 -54
- package/commands/ms/design-phase.md +33 -30
- package/commands/ms/review-design.md +106 -395
- package/mindsystem/references/principles.md +3 -3
- package/mindsystem/references/routing/next-phase-routing.md +1 -1
- package/mindsystem/references/scope-estimation.md +22 -35
- package/mindsystem/templates/design-iteration.md +13 -13
- package/mindsystem/templates/design.md +145 -327
- package/mindsystem/templates/knowledge.md +1 -1
- package/mindsystem/templates/milestone-archive.md +3 -3
- package/mindsystem/templates/phase-prompt.md +6 -7
- package/mindsystem/templates/research-subagent-prompt.md +2 -2
- package/mindsystem/templates/research.md +7 -7
- package/mindsystem/templates/roadmap.md +1 -1
- package/mindsystem/templates/verification-report.md +1 -1
- package/mindsystem/workflows/complete-milestone.md +52 -227
- package/mindsystem/workflows/discuss-phase.md +3 -3
- package/mindsystem/workflows/execute-plan.md +1 -1
- package/mindsystem/workflows/plan-phase.md +22 -50
- package/mindsystem/workflows/verify-phase.md +1 -1
- 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/scan-planning-context.py +186 -36
- package/scripts/validate-execution-order.sh +4 -5
- package/scripts/cleanup-phase-artifacts.sh +0 -68
|
@@ -343,6 +343,7 @@ def scan_debug_docs(
|
|
|
343
343
|
results.append(
|
|
344
344
|
{
|
|
345
345
|
"path": str(path),
|
|
346
|
+
"slug": path.stem,
|
|
346
347
|
"subsystem": fm.get("subsystem", ""),
|
|
347
348
|
"root_cause": fm.get("root_cause", ""),
|
|
348
349
|
"resolution": fm.get("resolution", ""),
|
|
@@ -552,6 +553,145 @@ def aggregate_from_summaries(
|
|
|
552
553
|
}
|
|
553
554
|
|
|
554
555
|
|
|
556
|
+
# ---------------------------------------------------------------------------
|
|
557
|
+
# Markdown formatting
|
|
558
|
+
# ---------------------------------------------------------------------------
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
def format_markdown(output: dict[str, Any]) -> str:
|
|
562
|
+
"""Format scanner output as readable markdown for LLM consumption."""
|
|
563
|
+
sections: list[str] = []
|
|
564
|
+
agg = output.get("aggregated", {})
|
|
565
|
+
|
|
566
|
+
# --- Established Patterns ---
|
|
567
|
+
patterns = agg.get("patterns_established", [])
|
|
568
|
+
if patterns:
|
|
569
|
+
lines = ["### Established Patterns"]
|
|
570
|
+
lines.extend(f"- {p}" for p in patterns)
|
|
571
|
+
sections.append("\n".join(lines))
|
|
572
|
+
|
|
573
|
+
# --- Tech Stack ---
|
|
574
|
+
tech = agg.get("tech_stack_added", [])
|
|
575
|
+
if tech:
|
|
576
|
+
sections.append(f"### Tech Stack\n{', '.join(tech)}")
|
|
577
|
+
|
|
578
|
+
# --- Key Decisions ---
|
|
579
|
+
decisions = agg.get("key_decisions", [])
|
|
580
|
+
if decisions:
|
|
581
|
+
lines = ["### Key Decisions"]
|
|
582
|
+
lines.extend(f"- {d}" for d in decisions)
|
|
583
|
+
sections.append("\n".join(lines))
|
|
584
|
+
|
|
585
|
+
# --- Key Files ---
|
|
586
|
+
created = agg.get("key_files_created", [])
|
|
587
|
+
modified = agg.get("key_files_modified", [])
|
|
588
|
+
if created or modified:
|
|
589
|
+
lines = ["### Key Files"]
|
|
590
|
+
if created:
|
|
591
|
+
lines.append("**Created:**")
|
|
592
|
+
lines.extend(f"- `{f}`" for f in created)
|
|
593
|
+
if modified:
|
|
594
|
+
lines.append("**Modified:**")
|
|
595
|
+
lines.extend(f"- `{f}`" for f in modified)
|
|
596
|
+
sections.append("\n".join(lines))
|
|
597
|
+
|
|
598
|
+
# --- Debug Learnings ---
|
|
599
|
+
debug = output.get("debug_learnings", [])
|
|
600
|
+
if debug:
|
|
601
|
+
lines = ["### Debug Learnings"]
|
|
602
|
+
for d in debug:
|
|
603
|
+
slug = d.get("slug", "unknown")
|
|
604
|
+
sub = d.get("subsystem", "")
|
|
605
|
+
rc = d.get("root_cause", "")
|
|
606
|
+
res = d.get("resolution", "")
|
|
607
|
+
lines.append(f"- **{slug}** ({sub}): {rc} — Fix: {res}")
|
|
608
|
+
sections.append("\n".join(lines))
|
|
609
|
+
|
|
610
|
+
# --- Adhoc Learnings ---
|
|
611
|
+
adhoc_entries = [
|
|
612
|
+
a for a in output.get("adhoc_learnings", []) if a.get("learnings")
|
|
613
|
+
]
|
|
614
|
+
if adhoc_entries:
|
|
615
|
+
lines = ["### Adhoc Learnings"]
|
|
616
|
+
for a in adhoc_entries:
|
|
617
|
+
sub = a.get("subsystem", "")
|
|
618
|
+
path = a.get("path", "")
|
|
619
|
+
label = sub or Path(path).stem if path else "unknown"
|
|
620
|
+
lines.append(f"- **{label}**")
|
|
621
|
+
for learning in a["learnings"]:
|
|
622
|
+
lines.append(f" - {learning}")
|
|
623
|
+
sections.append("\n".join(lines))
|
|
624
|
+
|
|
625
|
+
# --- Summaries ---
|
|
626
|
+
summaries = output.get("summaries", [])
|
|
627
|
+
needs_read = [
|
|
628
|
+
s
|
|
629
|
+
for s in summaries
|
|
630
|
+
if s.get("relevance") == "HIGH" and s.get("has_readiness_warnings")
|
|
631
|
+
]
|
|
632
|
+
other_relevant = [
|
|
633
|
+
s
|
|
634
|
+
for s in summaries
|
|
635
|
+
if s.get("relevance") in ("HIGH", "MEDIUM")
|
|
636
|
+
and not s.get("has_readiness_warnings")
|
|
637
|
+
]
|
|
638
|
+
|
|
639
|
+
if needs_read:
|
|
640
|
+
lines = ["### Summaries Needing Full Read"]
|
|
641
|
+
for s in needs_read:
|
|
642
|
+
lines.append(f"- `{s['path']}`")
|
|
643
|
+
sections.append("\n".join(lines))
|
|
644
|
+
|
|
645
|
+
if other_relevant:
|
|
646
|
+
lines = ["### Other Relevant Summaries"]
|
|
647
|
+
for s in other_relevant:
|
|
648
|
+
lines.append(f"- `{s['path']}` [{s.get('relevance', '')}]")
|
|
649
|
+
sections.append("\n".join(lines))
|
|
650
|
+
|
|
651
|
+
# --- Knowledge Files ---
|
|
652
|
+
matched_knowledge = [
|
|
653
|
+
k for k in output.get("knowledge_files", []) if k.get("matched")
|
|
654
|
+
]
|
|
655
|
+
if matched_knowledge:
|
|
656
|
+
lines = ["### Knowledge Files to Read"]
|
|
657
|
+
for k in matched_knowledge:
|
|
658
|
+
lines.append(f"- `{k['path']}`")
|
|
659
|
+
sections.append("\n".join(lines))
|
|
660
|
+
|
|
661
|
+
# --- Pending Todos ---
|
|
662
|
+
todos = output.get("pending_todos", [])
|
|
663
|
+
if todos:
|
|
664
|
+
lines = ["### Pending Todos"]
|
|
665
|
+
for t in todos:
|
|
666
|
+
title = t.get("title", "untitled")
|
|
667
|
+
priority = t.get("priority", "")
|
|
668
|
+
sub = t.get("subsystem", "")
|
|
669
|
+
path = t.get("path", "")
|
|
670
|
+
lines.append(f"- **{title}** [{priority}] ({sub}) — `{path}`")
|
|
671
|
+
sections.append("\n".join(lines))
|
|
672
|
+
|
|
673
|
+
# --- Scanner Info ---
|
|
674
|
+
sources = output.get("sources", {})
|
|
675
|
+
parse_errors = sources.get("parse_errors", [])
|
|
676
|
+
info_lines = ["### Scanner Info"]
|
|
677
|
+
for name, src in sources.items():
|
|
678
|
+
if name == "parse_errors" or not isinstance(src, dict):
|
|
679
|
+
continue
|
|
680
|
+
scanned = src.get("scanned", 0)
|
|
681
|
+
skipped = src.get("skipped")
|
|
682
|
+
if skipped:
|
|
683
|
+
info_lines.append(f"- {name}: skipped ({skipped})")
|
|
684
|
+
else:
|
|
685
|
+
info_lines.append(f"- {name}: {scanned} scanned")
|
|
686
|
+
if parse_errors:
|
|
687
|
+
info_lines.append("**Parse errors:**")
|
|
688
|
+
for err in parse_errors:
|
|
689
|
+
info_lines.append(f"- `{err.get('path', '')}`: {err.get('error', '')}")
|
|
690
|
+
sections.append("\n".join(info_lines))
|
|
691
|
+
|
|
692
|
+
return "\n\n".join(sections)
|
|
693
|
+
|
|
694
|
+
|
|
555
695
|
# ---------------------------------------------------------------------------
|
|
556
696
|
# Main
|
|
557
697
|
# ---------------------------------------------------------------------------
|
|
@@ -583,6 +723,11 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
583
723
|
default="",
|
|
584
724
|
help="Comma-separated keywords for tag matching",
|
|
585
725
|
)
|
|
726
|
+
parser.add_argument(
|
|
727
|
+
"--json",
|
|
728
|
+
action="store_true",
|
|
729
|
+
help="Output raw JSON (default: formatted markdown)",
|
|
730
|
+
)
|
|
586
731
|
return parser
|
|
587
732
|
|
|
588
733
|
|
|
@@ -604,40 +749,42 @@ def main() -> None:
|
|
|
604
749
|
|
|
605
750
|
planning = find_planning_dir()
|
|
606
751
|
if planning is None:
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
752
|
+
if args.json:
|
|
753
|
+
output: dict[str, Any] = {
|
|
754
|
+
"success": True,
|
|
755
|
+
"target": {
|
|
756
|
+
"phase": phase,
|
|
757
|
+
"phase_name": phase_name,
|
|
758
|
+
"subsystems": subsystems,
|
|
759
|
+
"keywords": keywords,
|
|
760
|
+
},
|
|
761
|
+
"sources": {
|
|
762
|
+
"summaries": {"dir": "", "scanned": 0, "skipped": ".planning/ not found"},
|
|
763
|
+
"debug_docs": {"dir": "", "scanned": 0, "skipped": ".planning/ not found"},
|
|
764
|
+
"adhoc_summaries": {"dir": "", "scanned": 0, "skipped": ".planning/ not found"},
|
|
765
|
+
"completed_todos": {"dir": "", "scanned": 0, "skipped": ".planning/ not found"},
|
|
766
|
+
"pending_todos": {"dir": "", "scanned": 0, "skipped": ".planning/ not found"},
|
|
767
|
+
"knowledge_files": {"dir": "", "scanned": 0, "skipped": ".planning/ not found"},
|
|
768
|
+
"parse_errors": [],
|
|
769
|
+
},
|
|
770
|
+
"summaries": [],
|
|
771
|
+
"debug_learnings": [],
|
|
772
|
+
"adhoc_learnings": [],
|
|
773
|
+
"completed_todos": [],
|
|
774
|
+
"pending_todos": [],
|
|
775
|
+
"knowledge_files": [],
|
|
776
|
+
"aggregated": {
|
|
777
|
+
"tech_stack_added": [],
|
|
778
|
+
"patterns_established": [],
|
|
779
|
+
"key_files_created": [],
|
|
780
|
+
"key_files_modified": [],
|
|
781
|
+
"key_decisions": [],
|
|
782
|
+
},
|
|
783
|
+
}
|
|
784
|
+
json.dump(output, sys.stdout, indent=2, cls=_SafeEncoder)
|
|
785
|
+
sys.stdout.write("\n")
|
|
786
|
+
else:
|
|
787
|
+
print("No .planning/ directory found. No prior context available.")
|
|
641
788
|
return
|
|
642
789
|
|
|
643
790
|
parse_errors: list[dict[str, str]] = []
|
|
@@ -681,8 +828,11 @@ def main() -> None:
|
|
|
681
828
|
"aggregated": aggregated,
|
|
682
829
|
}
|
|
683
830
|
|
|
684
|
-
|
|
685
|
-
|
|
831
|
+
if args.json:
|
|
832
|
+
json.dump(output, sys.stdout, indent=2, cls=_SafeEncoder)
|
|
833
|
+
sys.stdout.write("\n")
|
|
834
|
+
else:
|
|
835
|
+
print(format_markdown(output))
|
|
686
836
|
|
|
687
837
|
|
|
688
838
|
if __name__ == "__main__":
|
|
@@ -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
|