feat: integrate framework files into monorepo under packages/mosaic/framework/
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful

Moves all Mosaic framework runtime files from the separate bootstrap repo
into the monorepo as canonical source. The @mosaic/mosaic npm package now
ships the complete framework — bin scripts, runtime configs, tools, and
templates — enabling standalone installation via npm install.

Structure:
  packages/mosaic/framework/
  ├── bin/          28 CLI scripts (mosaic, mosaic-doctor, mosaic-sync-skills, etc.)
  ├── runtime/      Runtime adapters (claude, codex, opencode, pi, mcp)
  ├── tools/        Shell tooling (git, prdy, orchestrator, quality, etc.)
  ├── templates/    Agent and repo templates
  ├── defaults/     Default identity files (AGENTS.md, STANDARDS.md, SOUL.md, etc.)
  ├── install.sh    Legacy bash installer
  └── remote-install.sh  One-liner remote installer

Key files with Pi support and recent fixes:
- bin/mosaic: launch_pi() with skills-local loop
- bin/mosaic-doctor: --fix auto-wiring for all 4 harnesses
- bin/mosaic-sync-skills: Pi as 4th link target, symlink-aware find
- bin/mosaic-link-runtime-assets: Pi settings.json patching
- bin/mosaic-migrate-local-skills: Pi skill roots, symlink find
- runtime/pi/RUNTIME.md + mosaic-extension.ts

Package ships 251 framework files in the npm tarball (278KB compressed).
This commit is contained in:
Jason Woltje
2026-04-01 21:19:21 -05:00
parent f3cb3e6852
commit b38cfac760
252 changed files with 31477 additions and 1 deletions

View File

@@ -0,0 +1,305 @@
#!/bin/bash
# agent-lint.sh — Audit agent configuration across all coding projects
#
# Usage:
# agent-lint.sh # Scan all projects in ~/src/
# agent-lint.sh --project <path> # Scan single project
# agent-lint.sh --json # Output JSON for jarvis-brain
# agent-lint.sh --verbose # Show per-check details
# agent-lint.sh --fix-hint # Show fix commands for failures
#
# Checks per project:
# 1. Has runtime context file (CLAUDE.md or RUNTIME.md)?
# 2. Has AGENTS.md?
# 3. Runtime context file references conditional context/guides?
# 4. Runtime context file has quality gates?
# 5. For monorepos: sub-directories have AGENTS.md?
set -euo pipefail
# Defaults
SRC_DIR="$HOME/src"
SINGLE_PROJECT=""
JSON_OUTPUT=false
VERBOSE=false
FIX_HINT=false
# Exclusion patterns (not coding projects)
EXCLUDE_PATTERNS=(
"_worktrees"
".backup"
"_old"
"_bak"
"junk"
"traefik"
"infrastructure"
)
# Parse args
while [[ $# -gt 0 ]]; do
case "$1" in
--project) SINGLE_PROJECT="$2"; shift 2 ;;
--json) JSON_OUTPUT=true; shift ;;
--verbose) VERBOSE=true; shift ;;
--fix-hint) FIX_HINT=true; shift ;;
--src-dir) SRC_DIR="$2"; shift 2 ;;
-h|--help)
echo "Usage: agent-lint.sh [--project <path>] [--json] [--verbose] [--fix-hint] [--src-dir <dir>]"
exit 0
;;
*) echo "Unknown option: $1"; exit 1 ;;
esac
done
# Colors (disabled for JSON mode)
if $JSON_OUTPUT; then
GREEN="" RED="" YELLOW="" NC="" BOLD="" DIM=""
else
GREEN='\033[0;32m' RED='\033[0;31m' YELLOW='\033[0;33m'
NC='\033[0m' BOLD='\033[1m' DIM='\033[2m'
fi
# Determine if a directory is a coding project
is_coding_project() {
local dir="$1"
[[ -f "$dir/package.json" ]] || \
[[ -f "$dir/pyproject.toml" ]] || \
[[ -f "$dir/Cargo.toml" ]] || \
[[ -f "$dir/go.mod" ]] || \
[[ -f "$dir/Makefile" && -f "$dir/src/main.rs" ]] || \
[[ -f "$dir/pom.xml" ]] || \
[[ -f "$dir/build.gradle" ]]
}
# Check if directory should be excluded
is_excluded() {
local dir_name
dir_name=$(basename "$1")
for pattern in "${EXCLUDE_PATTERNS[@]}"; do
if [[ "$dir_name" == *"$pattern"* ]]; then
return 0
fi
done
return 1
}
# Detect if project is a monorepo
is_monorepo() {
local dir="$1"
[[ -f "$dir/pnpm-workspace.yaml" ]] || \
[[ -f "$dir/turbo.json" ]] || \
[[ -f "$dir/lerna.json" ]] || \
(grep -q '"workspaces"' "$dir/package.json" 2>/dev/null)
}
# Resolve runtime context file (CLAUDE.md or RUNTIME.md)
runtime_context_file() {
local dir="$1"
if [[ -f "$dir/CLAUDE.md" ]]; then
echo "$dir/CLAUDE.md"
return
fi
if [[ -f "$dir/RUNTIME.md" ]]; then
echo "$dir/RUNTIME.md"
return
fi
echo ""
}
# Check for runtime context file
check_runtime_context() {
[[ -n "$(runtime_context_file "$1")" ]]
}
# Check for AGENTS.md
check_agents_md() {
[[ -f "$1/AGENTS.md" ]]
}
# Check conditional loading/context (references guides or conditional section)
check_conditional_loading() {
local ctx
ctx="$(runtime_context_file "$1")"
[[ -n "$ctx" ]] && grep -qi "agent-guides\|~/.config/mosaic/guides\|conditional.*loading\|conditional.*documentation\|conditional.*context" "$ctx" 2>/dev/null
}
# Check quality gates
check_quality_gates() {
local ctx
ctx="$(runtime_context_file "$1")"
[[ -n "$ctx" ]] && grep -qi "quality.gates\|must pass before\|lint\|typecheck\|test" "$ctx" 2>/dev/null
}
# Check monorepo sub-AGENTS.md
check_monorepo_sub_agents() {
local dir="$1"
local missing=()
if ! is_monorepo "$dir"; then
echo "N/A"
return
fi
# Check apps/, packages/, services/, plugins/ directories
for subdir_type in apps packages services plugins; do
if [[ -d "$dir/$subdir_type" ]]; then
for subdir in "$dir/$subdir_type"/*/; do
[[ -d "$subdir" ]] || continue
# Only check if it has its own manifest
if [[ -f "$subdir/package.json" ]] || [[ -f "$subdir/pyproject.toml" ]]; then
if [[ ! -f "$subdir/AGENTS.md" ]]; then
missing+=("$(basename "$subdir")")
fi
fi
done
fi
done
if [[ ${#missing[@]} -eq 0 ]]; then
echo "OK"
else
echo "MISS:${missing[*]}"
fi
}
# Lint a single project
lint_project() {
local dir="$1"
local name
name=$(basename "$dir")
local has_runtime has_agents has_guides has_quality mono_status
local score=0 max_score=4
check_runtime_context "$dir" && has_runtime="OK" || has_runtime="MISS"
check_agents_md "$dir" && has_agents="OK" || has_agents="MISS"
check_conditional_loading "$dir" && has_guides="OK" || has_guides="MISS"
check_quality_gates "$dir" && has_quality="OK" || has_quality="MISS"
mono_status=$(check_monorepo_sub_agents "$dir")
[[ "$has_runtime" == "OK" ]] && ((score++)) || true
[[ "$has_agents" == "OK" ]] && ((score++)) || true
[[ "$has_guides" == "OK" ]] && ((score++)) || true
[[ "$has_quality" == "OK" ]] && ((score++)) || true
if $JSON_OUTPUT; then
cat <<JSONEOF
{
"project": "$name",
"path": "$dir",
"runtime_context": "$has_runtime",
"agents_md": "$has_agents",
"conditional_loading": "$has_guides",
"quality_gates": "$has_quality",
"monorepo_sub_agents": "$mono_status",
"score": $score,
"max_score": $max_score
}
JSONEOF
else
# Color-code the status
local c_runtime c_agents c_guides c_quality
[[ "$has_runtime" == "OK" ]] && c_runtime="${GREEN} OK ${NC}" || c_runtime="${RED} MISS ${NC}"
[[ "$has_agents" == "OK" ]] && c_agents="${GREEN} OK ${NC}" || c_agents="${RED} MISS ${NC}"
[[ "$has_guides" == "OK" ]] && c_guides="${GREEN} OK ${NC}" || c_guides="${RED} MISS ${NC}"
[[ "$has_quality" == "OK" ]] && c_quality="${GREEN} OK ${NC}" || c_quality="${RED} MISS ${NC}"
local score_color="$RED"
[[ $score -ge 3 ]] && score_color="$YELLOW"
[[ $score -eq 4 ]] && score_color="$GREEN"
printf " %-35s %b %b %b %b ${score_color}%d/%d${NC}" \
"$name" "$c_runtime" "$c_agents" "$c_guides" "$c_quality" "$score" "$max_score"
# Show monorepo status if applicable
if [[ "$mono_status" != "N/A" && "$mono_status" != "OK" ]]; then
printf " ${YELLOW}(mono: %s)${NC}" "$mono_status"
fi
echo ""
fi
if $VERBOSE && ! $JSON_OUTPUT; then
[[ "$has_runtime" == "MISS" ]] && echo " ${DIM} Runtime context file missing (CLAUDE.md or RUNTIME.md)${NC}"
[[ "$has_agents" == "MISS" ]] && echo " ${DIM} AGENTS.md missing${NC}"
[[ "$has_guides" == "MISS" ]] && echo " ${DIM} No conditional context/loading section detected${NC}"
[[ "$has_quality" == "MISS" ]] && echo " ${DIM} No quality gates section${NC}"
if [[ "$mono_status" == MISS:* ]]; then
echo " ${DIM} Monorepo sub-AGENTS.md missing: ${mono_status#MISS:}${NC}"
fi
fi
if $FIX_HINT && ! $JSON_OUTPUT; then
if [[ "$has_runtime" == "MISS" || "$has_agents" == "MISS" ]]; then
echo " ${DIM}Fix: ~/.config/mosaic/tools/bootstrap/init-project.sh --name \"$name\" --type auto${NC}"
elif [[ "$has_guides" == "MISS" ]]; then
echo " ${DIM}Fix: ~/.config/mosaic/tools/bootstrap/agent-upgrade.sh $dir --section conditional-loading${NC}"
fi
fi
# Return score for summary
echo "$score" > /tmp/agent-lint-score-$$
}
# Main
main() {
local projects=()
local total=0 passing=0 total_score=0
if [[ -n "$SINGLE_PROJECT" ]]; then
projects=("$SINGLE_PROJECT")
else
for dir in "$SRC_DIR"/*/; do
[[ -d "$dir" ]] || continue
is_excluded "$dir" && continue
is_coding_project "$dir" && projects+=("${dir%/}")
done
fi
if [[ ${#projects[@]} -eq 0 ]]; then
echo "No coding projects found."
exit 0
fi
if $JSON_OUTPUT; then
echo '{ "audit_date": "'$(date -I)'", "projects": ['
local first=true
for dir in "${projects[@]}"; do
$first || echo ","
first=false
lint_project "$dir"
done
echo '] }'
else
echo ""
echo -e "${BOLD}Agent Configuration Audit — $(date +%Y-%m-%d)${NC}"
echo "========================================================"
printf " %-35s %s %s %s %s %s\n" \
"Project" "RUNTIME" "AGENTS" "Guides" "Quality" "Score"
echo " -----------------------------------------------------------------------"
for dir in "${projects[@]}"; do
lint_project "$dir"
local score
score=$(cat /tmp/agent-lint-score-$$ 2>/dev/null || echo 0)
((total++)) || true
((total_score += score)) || true
[[ $score -eq 4 ]] && ((passing++)) || true
done
rm -f /tmp/agent-lint-score-$$
echo " -----------------------------------------------------------------------"
local need_attention=$((total - passing))
echo ""
echo -e " ${BOLD}Summary:${NC} $total projects | ${GREEN}$passing pass${NC} | ${RED}$need_attention need attention${NC}"
echo ""
if [[ $need_attention -gt 0 ]] && ! $FIX_HINT; then
echo -e " ${DIM}Run with --fix-hint for suggested fixes${NC}"
echo -e " ${DIM}Run with --verbose for per-check details${NC}"
echo ""
fi
fi
}
main