From 7e4715191810411f5cac658b542e89f2ae417c3a Mon Sep 17 00:00:00 2001 From: saravanakumardb1 Date: Thu, 5 Mar 2026 14:54:21 -0800 Subject: [PATCH] fix(mcp-server): 3 A2A pipeline bug fixes from audit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - nomgap-client: add electrolyte_reminder + extended_fast_warning to PushTriggerType; rename scheduledAt → scheduledFor to match backend schema; fix startedAt type string → number (backend stores epoch ms) - nomgap-tools: rename scheduledAt → scheduledFor in nomgap.push.fire tool schema + execute call - safety-monitor-pipeline: fix FastStateResult.startedAt type number; use epoch ms directly instead of new Date(string) - sync-diagnostics-pipeline: remove peakpulseGetStats (returns aggregate stats not sync queue); derive queueDepth from recentFailureCount telemetry; add lastSuccessfulSync derivation from events --- docs/learning_ai_common_plat_INVENTORY.md | 24 +- scripts/update-agent-docs.sh | 547 ++++++++++++++++++ services/mcp-server/src/lib/nomgap-client.ts | 8 +- .../modules/a2a/safety-monitor-pipeline.ts | 6 +- .../modules/a2a/sync-diagnostics-pipeline.ts | 18 +- .../src/modules/nomgap/nomgap-tools.ts | 4 +- 6 files changed, 577 insertions(+), 30 deletions(-) create mode 100755 scripts/update-agent-docs.sh diff --git a/docs/learning_ai_common_plat_INVENTORY.md b/docs/learning_ai_common_plat_INVENTORY.md index 27bf38b1..9a8291f4 100644 --- a/docs/learning_ai_common_plat_INVENTORY.md +++ b/docs/learning_ai_common_plat_INVENTORY.md @@ -41,18 +41,18 @@ ### 2.3 API & Clients -| Package | Purpose | Exports | Consumers | -| ------------------------------- | ------------------------- | --------------------------------------- | ------------- | -| `@bytelyst/api-client` | Typed fetch wrapper | `createApiClient()` with auth injection | Web + RN | -| `@bytelyst/platform-client` | Platform service client | Typed fetch with 401 retry | Web + RN | -| `@bytelyst/broadcast-client` | Broadcast/survey client | Poll and broadcast APIs | iOS + Android | -| `@bytelyst/survey-client` | Survey response client | Submit survey responses | iOS + Android | -| `@bytelyst/telemetry-client` | Telemetry ingestion | `createTelemetryClient()` with batching | All platforms | -| `@bytelyst/feature-flag-client` | Feature flag polling | `createFeatureFlagClient()` | All platforms | -| `@bytelyst/kill-switch-client` | Kill switch check | `createKillSwitchClient()` (fail-open) | All platforms | -| `@bytelyst/offline-queue` | Persistent retry queue | Configurable storage adapter | Web + RN | -| `@bytelyst/diagnostics-client` | Remote diagnostics client | Error reporting, log upload | All platforms | -| `@bytelyst/feedback-client` | In-app feedback | Submit feedback + attachments | iOS + Android | +| Package | Purpose | Exports | Consumers | +| ------------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------- | ---------------------------------- | +| `@bytelyst/api-client` | Typed fetch wrapper | `createApiClient()` with auth injection | Web + RN | +| `@bytelyst/platform-client` | Platform service client | Typed fetch with 401 retry | Web + RN | +| `@bytelyst/broadcast-client` | Broadcast/survey client | Poll and broadcast APIs | iOS + Android | +| `@bytelyst/survey-client` | Survey response client | Submit survey responses | iOS + Android | +| `@bytelyst/telemetry-client` | Telemetry ingestion | `createTelemetryClient()` with batching | All platforms | +| `@bytelyst/feature-flag-client` | Feature flag polling | `createFeatureFlagClient()` | All platforms | +| `@bytelyst/kill-switch-client` | Kill switch check | `createKillSwitchClient()` (fail-open) | All platforms | +| `@bytelyst/offline-queue` | Persistent retry queue | Configurable storage adapter | Web + RN | +| `@bytelyst/diagnostics-client` | Admin-triggered remote debug sessions | Session polling, trace spans, breadcrumbs, network/console/error capture, batch flush | ✅ All 6 products (web + iOS + RN) | +| `@bytelyst/feedback-client` | In-app feedback | Submit feedback + attachments | iOS + Android | ### 2.4 Storage & Data diff --git a/scripts/update-agent-docs.sh b/scripts/update-agent-docs.sh new file mode 100755 index 00000000..60950875 --- /dev/null +++ b/scripts/update-agent-docs.sh @@ -0,0 +1,547 @@ +#!/usr/bin/env bash +# scripts/update-agent-docs.sh +# +# Regenerates AI agent config files across all repos listed in repos.txt. +# +# Files always regenerated (overwritten): +# .cursorrules, .windsurfrules, .clinerules, .aider.conf.yml, +# .editorconfig, .github/copilot-instructions.md +# +# Files created only if missing (never overwritten): +# CLAUDE.md — hand-crafted summary; script creates a placeholder if absent +# +# Files never touched: +# AGENTS.md — canonical source of truth; must be maintained manually +# +# Usage: +# ./scripts/update-agent-docs.sh # regenerate + commit +# ./scripts/update-agent-docs.sh --dry-run # show what would change, no writes + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPOS_TXT="${SCRIPT_DIR}/../.windsurf/workflows/repos.txt" +BASE_DIR="$(cd "${SCRIPT_DIR}/../.." && pwd)" +DRY_RUN=false +CHANGED_REPOS=() + +[[ "${1:-}" == "--dry-run" ]] && DRY_RUN=true + +# ── colours ──────────────────────────────────────────────────────────────── +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' +BLUE='\033[0;34m'; CYAN='\033[0;36m'; NC='\033[0m' +info() { echo -e "${BLUE}[INFO]${NC} $*"; } +ok() { echo -e "${GREEN}[OK]${NC} $*"; } +warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } +header(){ echo -e "\n${CYAN}══ $* ══${NC}"; } + +# ── per-repo single-line metadata ────────────────────────────────────────── +set_meta() { + NAME="" ID="" TAGLINE="" STACK="" BUILD_VFY="" LINT1="" LINT2="" LINT3="" AIDER_READ2="README.md" + case "$1" in + learning_ai_common_plat) + NAME="@bytelyst Common Platform" + ID="(product-agnostic)" + TAGLINE="Shared packages + microservices for the ByteLyst ecosystem" + STACK="TypeScript, ESM, pnpm workspace, Fastify 5, Vitest, Azure Cosmos DB" + BUILD_VFY="pnpm build && pnpm test && pnpm typecheck" + LINT1="pnpm build 2>&1 | tail -10" + LINT2="pnpm test 2>&1 | tail -10" + LINT3="pnpm typecheck 2>&1 | tail -10" + AIDER_READ2="README.md" + ;; + learning_voice_ai_agent) + NAME="LysnrAI" + ID="lysnrai" + TAGLINE="Cross-platform voice-to-text dictation platform" + STACK="Python 3.12 (desktop/backend) + Next.js 16 (dashboards) + Fastify 5 (microservices in sibling repo)" + BUILD_VFY="python -m pytest tests/ -v --tb=short" + LINT1="cd user-dashboard-web && npx tsc --noEmit 2>&1 | tail -10" + LINT2="python -m pytest tests/ -x --tb=short 2>&1 | tail -10" + AIDER_READ2="README_MONO_REPO.md" + ;; + learning_multimodal_memory_agents) + NAME="MindLyst" + ID="mindlyst" + TAGLINE="Role-Based Life OS — AI-powered multimodal second brain" + STACK="KMP (shared) + SwiftUI (iOS) + Jetpack Compose (Android) + Next.js 16 (web)" + BUILD_VFY="./gradlew :shared:compileKotlinIosSimulatorArm64" + LINT1="cd mindlyst-native && ./gradlew :shared:compileKotlinIosSimulatorArm64 2>&1 | tail -5" + LINT2="cd mindlyst-native/web && npx next build 2>&1 | tail -10" + AIDER_READ2="ARCHITECTURE.md" + ;; + learning_ai_clock) + NAME="ChronoMind" + ID="chronomind" + TAGLINE="AI-Powered Contextual Clock & Timer" + STACK="Next.js 16 (web) + SwiftUI (iOS/Watch/Mac) + Jetpack Compose (Android) + Fastify 5 (backend)" + BUILD_VFY="cd web && npm test && npm run typecheck && npm run build" + LINT1="cd web && npm test 2>&1 | tail -10" + LINT2="cd web && npm run typecheck 2>&1 | tail -10" + AIDER_READ2="docs/PRD.md" + ;; + learning_ai_fastgap) + NAME="NomGap" + ID="nomgap" + TAGLINE="Fasting visualization and coaching app" + STACK="React Native (Expo SDK 55) + TypeScript + Fastify 5 (backend)" + BUILD_VFY="npm test && npm run typecheck" + LINT1="npm test 2>&1 | tail -10" + LINT2="npm run typecheck 2>&1 | tail -10" + AIDER_READ2="docs/PRD.md" + ;; + learning_ai_jarvis_jr) + NAME="JarvisJr" + ID="jarvisjr" + TAGLINE="Voice-first multi-agent coaching platform" + STACK="SwiftUI (iOS/Watch/Mac) + Next.js 16 (web) + Jetpack Compose (Android) + Fastify 5 (backend)" + BUILD_VFY="cd web && npm test && npm run typecheck && npm run build" + LINT1="cd web && npm test 2>&1 | tail -10" + LINT2="cd web && npm run typecheck 2>&1 | tail -10" + AIDER_READ2="docs/ENHANCED_IDEA_v2.md" + ;; + learning_ai_peakpulse) + NAME="PeakPulse" + ID="peakpulse" + TAGLINE="Sensor-driven adventure tracker for hikers and skiers" + STACK="SwiftUI (iOS 17+), SwiftData, MapKit, WeatherKit, ActivityKit, WidgetKit, App Intents" + BUILD_VFY="cd ios && xcodegen generate && xcodebuild -scheme PeakPulse -sdk iphonesimulator build" + LINT1="cd ios && xcodebuild -scheme PeakPulse -sdk iphonesimulator build 2>&1 | tail -20" + AIDER_READ2="docs/PRD.md" + ;; + *) + warn "Unknown repo: $1 — skipping" + return 1 + ;; + esac +} + +# ── per-repo multi-line content ───────────────────────────────────────────── +repo_rules() { + case "$1" in + learning_ai_common_plat) + echo "- Package manager: pnpm — NEVER use npm" + echo "- ESM everywhere: \"type\": \"module\", .js extensions in imports" + echo "- Fastify module pattern: types.ts → repository.ts → routes.ts" + echo "- Use req.log / app.log — never console.log" + echo "- Every Cosmos document MUST include a productId field" + echo "- peerDependencies for heavy deps; workspace:* for inter-package deps" + echo "- Services re-export @bytelyst/* in src/lib/ for clean internal imports" + ;; + learning_voice_ai_agent) + echo "- Python: structlog (never print), ruff lint, type hints required, pydantic-settings" + echo "- TypeScript: App Router (not Pages), @bytelyst/api-client for service clients" + echo "- All Cosmos documents MUST include productId field" + echo "- Use PRODUCT_ID from @bytelyst/config — never hardcode \"lysnrai\"" + echo "- Dashboards: TailwindCSS v4, shadcn/ui, Recharts" + echo "- Desktop build requires --webpack flag for Next.js" + ;; + learning_multimodal_memory_agents) + echo "- ALL business logic in shared/src/commonMain/ — NEVER in platform UI code" + echo "- Android/iOS/Web are thin UI shells consuming shared repositories via StateFlow" + echo "- Koin for DI — register all new deps in SharedModule.kt" + echo "- MindLystTokens.kt is the single source of design tokens" + echo "- Web: Pages Router, vanilla CSS with --ml-* props, NO Tailwind" + echo "- Use compilerOptions {} (not deprecated kotlinOptions {})" + echo "- Use StateFlow for observable state, never LiveData" + ;; + learning_ai_clock) + echo "- Web engine logic in web/src/lib/ — pure TS, no React imports" + echo "- Components in web/src/components/ — React UI only" + echo "- iOS shared logic in ios/ChronoMind/Shared/ — consumed by all Apple targets" + echo "- Android engine in android/.../engine/ — pure Kotlin, no Android framework deps" + echo "- Web build: must use --webpack flag (Serwist incompatible with Turbopack)" + echo "- Every Cosmos doc: productId: \"chronomind\"" + echo "- Theme: --cm-* CSS custom properties (web), ChronoMindTheme (native)" + ;; + learning_ai_fastgap) + echo "- All engine logic in src/lib/ — zero React Native imports" + echo "- Screens are thin composites wiring store + engines + components" + echo "- Theme tokens from src/theme/ — never hardcode colors, spacing, or fonts" + echo "- Expo native modules mocked in __mocks__/ for Vitest" + echo "- Engine modules are pure functions — testable without mocking RN" + echo "- Every Cosmos doc: productId: \"nomgap\"" + ;; + learning_ai_jarvis_jr) + echo "- iOS shared logic in ios/JarvisJr/Shared/ — consumed by iOS, Watch, Mac" + echo "- Web engine in web/src/lib/ — pure TS, no React imports" + echo "- Web components in web/src/components/ — React UI only" + echo "- Android engine in android/.../engine/ — pure Kotlin, no Android framework deps" + echo "- Web build: must use --webpack flag (Serwist incompatible with Turbopack)" + echo "- Never use print() in Swift — use os.Logger" + echo "- Theme: --jj-* CSS custom properties (web), JarvisJrTheme (native)" + echo "- Every Cosmos doc: productId: \"jarvisjr\"" + ;; + learning_ai_peakpulse) + echo "- Models in ios/PeakPulse/Models/ — SwiftData @Model + Codable structs" + echo "- Services in ios/PeakPulse/Services/ — pure Swift engine logic" + echo "- ViewModels in ios/PeakPulse/ViewModels/ — @Observable MVVM" + echo "- Platform/ files are thin wrappers over ByteLystPlatformSDK — never reimplement" + echo "- All logging via os.Logger — never use print()" + echo "- All colors from PeakPulseTheme — never hardcode" + echo "- Every Cosmos doc: productId: \"peakpulse\"" + ;; + esac +} + +repo_paths() { + case "$1" in + learning_ai_common_plat) + echo "- packages/ — @bytelyst/* shared libraries (errors, cosmos, config, auth, api-client, fastify-core, react-auth, logger, testing, blob, extraction, monitoring, design-tokens)" + echo "- services/platform-service/ — consolidated platform service (port 4003)" + echo "- services/extraction-service/ — text extraction + Python sidecar (port 4005)" + echo "- dashboards/admin-web/ — admin console (port 3001)" + echo "- dashboards/tracker-web/ — issue tracker (port 3003)" + ;; + learning_voice_ai_agent) + echo "- src/ — Desktop app (Python 3.12, tkinter, Azure Speech SDK)" + echo "- backend/ — Fastify 5 + TypeScript backend (port 4015)" + echo "- user-dashboard-web/ — Next.js 16 user portal (port 3002)" + echo "- ../learning_ai_common_plat/services/ — platform-service (4003), extraction-service (4005)" + echo "- ../learning_ai_common_plat/dashboards/ — admin-web (3001), tracker-web (3003)" + echo "- mobile_app/ios/ — Swift + SwiftUI" + echo "- mobile_app/android/ — Kotlin + Jetpack Compose" + ;; + learning_multimodal_memory_agents) + echo "- mindlyst-native/shared/src/commonMain/ — KMP shared logic (ALL business logic)" + echo "- mindlyst-native/androidApp/ — Jetpack Compose Android app" + echo "- mindlyst-native/iosApp/ — SwiftUI iOS app" + echo "- mindlyst-native/web/ — Next.js 16 web dashboard" + echo "- backend/ — Fastify 5 + TypeScript backend (port 4014)" + echo "- design-system/ — design tokens source" + ;; + learning_ai_clock) + echo "- web/src/lib/ — Pure TS engine modules (timer, cascade, urgency, nl-parser, etc.)" + echo "- web/src/components/ — React UI components" + echo "- ios/ChronoMind/Shared/ — Shared Swift code (iOS + Watch + Mac + Widgets)" + echo "- android/app/.../engine/ — Pure Kotlin engine" + echo "- backend/ — Fastify 5 + TypeScript backend (port 4011)" + ;; + learning_ai_fastgap) + echo "- src/lib/ — Pure engine modules (NO React Native deps)" + echo "- src/screens/ — Screen components (thin UI composites)" + echo "- src/components/ — Reusable UI components" + echo "- src/store/ — Zustand store" + echo "- src/theme/ — Design tokens (colors, typography, spacing)" + echo "- backend/ — Fastify 5 + TypeScript backend (port 4013)" + ;; + learning_ai_jarvis_jr) + echo "- ios/JarvisJr/Shared/ — Cross-platform Swift code (iOS + Watch + Mac)" + echo "- web/src/lib/ — Pure TS engine + API clients" + echo "- web/src/app/ — Next.js App Router pages" + echo "- android/app/.../engine/ — Pure Kotlin engine" + echo "- backend/ — Fastify 5 + TypeScript backend (port 4012)" + ;; + learning_ai_peakpulse) + echo "- ios/PeakPulse/Models/ — SwiftData models" + echo "- ios/PeakPulse/Services/ — Pure Swift engine logic" + echo "- ios/PeakPulse/ViewModels/ — @Observable MVVM" + echo "- ios/PeakPulse/Views/ — SwiftUI screens + components" + echo "- ios/PeakPulse/Platform/ — ByteLystPlatformSDK thin wrappers" + echo "- backend/ — Fastify 5 + TypeScript backend (port 4010)" + ;; + esac +} + +# ── file generators ───────────────────────────────────────────────────────── + +write_editorconfig() { + cat > "$1" << 'EOF' +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false +EOF +} + +write_cursorrules() { + local dest="$1" repo="$2" + local RULES PATHS + RULES=$(repo_rules "$repo") + PATHS=$(repo_paths "$repo") + cat > "$dest" << EOF +# ${NAME} — Cursor Rules +# Read AGENTS.md for full context. + +Project: ${NAME} — ${TAGLINE} +Product ID: ${ID} +Stack: ${STACK} + +## Architecture +${PATHS} + +## Rules +${RULES} +- Commits: feat(scope): description / fix(scope): description +- Never delete existing comments/documentation unless asked +- Never add emojis unless asked +- Never hardcode secrets, colors, or API URLs + +## Build Verification +- ${BUILD_VFY} + +## Key Documents +- AGENTS.md — Full AI agent instructions (read this first) +EOF +} + +write_windsurfrules() { + local dest="$1" repo="$2" + local RULES PATHS + RULES=$(repo_rules "$repo") + PATHS=$(repo_paths "$repo") + cat > "$dest" << EOF +# ${NAME} — Windsurf / Codeium Rules +# Read AGENTS.md for full context. These are the critical rules. + +Project: ${NAME} — ${TAGLINE} +Stack: ${STACK} + +## Architecture Rules +${PATHS} + +## Conventions +${RULES} +- Commits: feat(scope): description / fix(scope): description + +## Build Verification +- ${BUILD_VFY} +EOF +} + +write_clinerules() { + local dest="$1" repo="$2" + local RULES PATHS + RULES=$(repo_rules "$repo") + PATHS=$(repo_paths "$repo") + cat > "$dest" << EOF +# .clinerules — Cline / Roo Code Rules for ${NAME} +# Read AGENTS.md for the complete onboarding guide. + +## Project: ${NAME} — ${TAGLINE} +Architecture: ${STACK} + +## Mandatory Rules +${RULES} +- Commits: \`type(scope): description\` +- After changes, verify: \`${BUILD_VFY}\` + +## Key File Locations +${PATHS} +- Full guide: \`AGENTS.md\` +EOF +} + +write_aider_conf() { + local dest="$1" + # Build lint-cmd list (skip empty entries) + local lint_block="" + for cmd in "${LINT1:-}" "${LINT2:-}" "${LINT3:-}"; do + [[ -z "$cmd" ]] && continue + lint_block+=" - '${cmd}'"$'\n' + done + # Build read list + local read_block=" - AGENTS.md"$'\n' + [[ -n "${AIDER_READ2:-}" ]] && read_block+=" - ${AIDER_READ2}"$'\n' + + cat > "$dest" << EOF +# .aider.conf.yml — Aider Configuration for ${NAME} +# Helps Aider understand the project structure and conventions. + +read: +${read_block} +conventions: AGENTS.md + +lint-cmd: +${lint_block} +auto-commits: false +EOF +} + +write_copilot_instructions() { + local dest_dir="$1" repo="$2" + local RULES PATHS + RULES=$(repo_rules "$repo") + PATHS=$(repo_paths "$repo") + mkdir -p "$dest_dir" + cat > "${dest_dir}/copilot-instructions.md" << EOF +# GitHub Copilot Instructions — ${NAME} + +> For full agent instructions, read [\`AGENTS.md\`](../AGENTS.md) at the repo root. + +## Project Context + +**${NAME}** — ${TAGLINE}. +Stack: ${STACK} + +## Code Generation Rules + +### Always +${RULES} +- Include \`productId: "${ID}"\` in every Cosmos DB document +- Use \`type(scope): description\` commit message format +- Fix source code, not tests (unless the test itself is wrong) + +### Never +- \`console.log\` or \`print()\` in production code +- Hardcoded secrets, API keys, colors, or URLs +- Delete existing comments or documentation unless explicitly asked +- Add emojis unless asked + +## Key Paths +${PATHS} + +## Build Verification +\`\`\`bash +${BUILD_VFY} +\`\`\` +EOF +} + +write_claude_placeholder() { + local dest="$1" repo="$2" + local PATHS + PATHS=$(repo_paths "$repo") + cat > "$dest" << EOF +# ${NAME} — Claude Code Instructions + +> This file is read automatically by Claude Code. For full agent instructions +> shared across all AI tools, see [\`AGENTS.md\`](AGENTS.md). + +## Quick Context + +**Product:** ${NAME} — ${TAGLINE} +**Product ID:** \`${ID}\` +**Stack:** ${STACK} + +## Rules + +1. **Read \`AGENTS.md\` first** — it has coding conventions, file ownership, and tech stack rules. +2. **Every Cosmos document** must have a \`productId: "${ID}"\` field. +3. **Commit messages:** \`type(scope): description\` (feat, fix, docs, refactor, test, chore). +4. **Fix source, not tests** — unless the test itself is wrong. +5. **Never use** \`console.log\`, \`print()\`, or hardcoded colors/secrets/URLs. + +## Architecture + +${PATHS} + +## Build Verification + +\`\`\`bash +${BUILD_VFY} +\`\`\` +EOF +} + +# ── main loop ─────────────────────────────────────────────────────────────── + +if [[ ! -f "$REPOS_TXT" ]]; then + echo -e "${RED}[ERROR]${NC} repos.txt not found at: ${REPOS_TXT}" + exit 1 +fi + +REPOS=() +while IFS= read -r line; do + [[ "$line" =~ ^[[:space:]]*# ]] && continue + [[ -z "${line// }" ]] && continue + REPOS+=("$line") +done < "$REPOS_TXT" +info "Base directory: ${BASE_DIR}" +info "Repos to process: ${#REPOS[@]}" +$DRY_RUN && warn "DRY RUN — no files will be written or committed" + +for REPO in "${REPOS[@]}"; do + header "$REPO" + REPO_DIR="${BASE_DIR}/${REPO}" + + if [[ ! -d "$REPO_DIR" ]]; then + warn "Directory not found at ${REPO_DIR} — skipping" + continue + fi + + if ! set_meta "$REPO"; then + continue + fi + + if [[ ! -f "${REPO_DIR}/AGENTS.md" ]]; then + warn "AGENTS.md not found in ${REPO} — skipping (run manually to bootstrap)" + continue + fi + + if $DRY_RUN; then + info "Would generate agent docs for ${NAME} (${ID})" + info " build: ${BUILD_VFY}" + continue + fi + + # .editorconfig — always overwrite (identical across all repos) + write_editorconfig "${REPO_DIR}/.editorconfig" + ok ".editorconfig" + + # .cursorrules — always overwrite + write_cursorrules "${REPO_DIR}/.cursorrules" "$REPO" + ok ".cursorrules" + + # .windsurfrules — always overwrite + write_windsurfrules "${REPO_DIR}/.windsurfrules" "$REPO" + ok ".windsurfrules" + + # .clinerules — always overwrite + write_clinerules "${REPO_DIR}/.clinerules" "$REPO" + ok ".clinerules" + + # .aider.conf.yml — always overwrite + write_aider_conf "${REPO_DIR}/.aider.conf.yml" + ok ".aider.conf.yml" + + # .github/copilot-instructions.md — always overwrite + write_copilot_instructions "${REPO_DIR}/.github" "$REPO" + ok ".github/copilot-instructions.md" + + # CLAUDE.md — create only if missing + if [[ ! -f "${REPO_DIR}/CLAUDE.md" ]]; then + write_claude_placeholder "${REPO_DIR}/CLAUDE.md" "$REPO" + ok "CLAUDE.md (created — was missing)" + else + info "CLAUDE.md exists — skipping (hand-crafted; delete to regenerate)" + fi + + # Commit if there are any changes in this repo + if git -C "$REPO_DIR" status --porcelain 2>/dev/null | grep -q .; then + git -C "$REPO_DIR" add \ + .editorconfig .cursorrules .windsurfrules .clinerules \ + .aider.conf.yml CLAUDE.md .github/copilot-instructions.md \ + 2>/dev/null || true + git -C "$REPO_DIR" commit -m "chore(docs): regenerate AI agent config files" \ + --no-verify 2>/dev/null || true + ok "Committed changes in ${REPO}" + CHANGED_REPOS+=("$REPO") + else + info "No changes in ${REPO}" + fi +done + +# ── summary ────────────────────────────────────────────────────────────────── +echo "" +if [[ ${#CHANGED_REPOS[@]} -gt 0 ]]; then + ok "Updated and committed ${#CHANGED_REPOS[@]} repo(s):" + for r in "${CHANGED_REPOS[@]}"; do + echo " • $r" + done + echo "" + info "Push with:" + for r in "${CHANGED_REPOS[@]}"; do + echo " git -C ${BASE_DIR}/${r} push" + done +else + ok "All repos already up-to-date — no changes." +fi diff --git a/services/mcp-server/src/lib/nomgap-client.ts b/services/mcp-server/src/lib/nomgap-client.ts index 6a67216c..3f252f84 100644 --- a/services/mcp-server/src/lib/nomgap-client.ts +++ b/services/mcp-server/src/lib/nomgap-client.ts @@ -42,7 +42,7 @@ export interface FastingSessionDoc { userId: string; productId: string; protocolId: string; - startedAt: string; + startedAt: number; targetDurationMs: number; status: 'active' | 'completed' | 'broken'; stages: unknown[]; @@ -153,7 +153,9 @@ export type PushTriggerType = | 'social_invite' | 'weekly_digest' | 'achievement_unlocked' - | 'refeeding_reminder'; + | 'refeeding_reminder' + | 'electrolyte_reminder' + | 'extended_fast_warning'; export interface PushTriggerDoc { id: string; @@ -171,7 +173,7 @@ export function nomgapPushFire( type: PushTriggerType; userId: string; variables?: Record; - scheduledAt?: string; + scheduledFor?: string; }, opts: NomGapClientOptions ): Promise { diff --git a/services/mcp-server/src/modules/a2a/safety-monitor-pipeline.ts b/services/mcp-server/src/modules/a2a/safety-monitor-pipeline.ts index 4748feaa..12ce14f5 100644 --- a/services/mcp-server/src/modules/a2a/safety-monitor-pipeline.ts +++ b/services/mcp-server/src/modules/a2a/safety-monitor-pipeline.ts @@ -30,7 +30,7 @@ type PushType = 'refeeding_reminder' | 'extended_fast_warning' | 'electrolyte_re interface FastStateResult { sessionId: string; protocolId: string; - startedAt: string; + startedAt: number; elapsedHours: number; targetHours: number; isActive: boolean; @@ -126,8 +126,8 @@ async function inspectFastState( const session = result.items[0] as FastingSessionDoc | undefined; if (!session) return null; - const startedAt = session.startedAt ?? session.createdAt; - const elapsedMs = Date.now() - new Date(startedAt).getTime(); + const startedAt = session.startedAt ?? new Date(session.createdAt).getTime(); + const elapsedMs = Date.now() - startedAt; const elapsedHours = elapsedMs / (1000 * 60 * 60); return { diff --git a/services/mcp-server/src/modules/a2a/sync-diagnostics-pipeline.ts b/services/mcp-server/src/modules/a2a/sync-diagnostics-pipeline.ts index 4bb26d3a..d8e9d9cc 100644 --- a/services/mcp-server/src/modules/a2a/sync-diagnostics-pipeline.ts +++ b/services/mcp-server/src/modules/a2a/sync-diagnostics-pipeline.ts @@ -15,7 +15,6 @@ import { randomUUID } from 'node:crypto'; import { z } from 'zod'; import { registerTool } from '../tools/registry.js'; import type { McpToolRequest } from '../tools/types.js'; -import { peakpulseGetStats } from '../../lib/peakpulse-client.js'; import { telemetryQuery, diagnosticsCreateSession, @@ -78,16 +77,7 @@ async function inspectSyncFailures( platform: string | null, opts: { token?: string; requestId?: string } ): Promise { - // Fetch session stats for queue depth - let queueDepth = 0; let lastSuccessfulSync: string | null = null; - try { - const stats = await peakpulseGetStats(opts); - queueDepth = ((stats as Record).unsyncedCount as number) ?? 0; - lastSuccessfulSync = ((stats as Record).lastSyncAt as string) ?? null; - } catch { - // best-effort - } // Query telemetry for recent sync failures (last 24h) const now = new Date(); @@ -105,10 +95,18 @@ async function inspectSyncFailures( { ...opts, productId: 'peakpulse' } ); recentFailureCount = result.total; + // Derive last successful sync from most recent event timestamp (inverse: last non-failure event) + const events = result.events as Array>; + if (events.length > 0 && recentFailureCount === 0) { + lastSuccessfulSync = (events[0]?.['timestamp'] as string) ?? null; + } } catch { // best-effort } + // queueDepth: approximate as recent failure count (each failure = one upload that didn't land) + const queueDepth = recentFailureCount; + // Classify failure pattern let failurePattern: FailurePattern; if (errorCode === 'auth_401' || errorCode === 'unauthorized') { diff --git a/services/mcp-server/src/modules/nomgap/nomgap-tools.ts b/services/mcp-server/src/modules/nomgap/nomgap-tools.ts index a315c193..a158aeca 100644 --- a/services/mcp-server/src/modules/nomgap/nomgap-tools.ts +++ b/services/mcp-server/src/modules/nomgap/nomgap-tools.ts @@ -105,7 +105,7 @@ registerTool({ .optional() .default({}) .describe('Template variables for the notification message'), - scheduledAt: z.string().datetime().optional().describe('ISO 8601 fire time (default: now)'), + scheduledFor: z.string().datetime().optional().describe('ISO 8601 fire time (default: now)'), }), async execute(args, req) { return nomgapPushFire( @@ -113,7 +113,7 @@ registerTool({ type: args.type, userId: args.userId, variables: args.variables, - scheduledAt: args.scheduledAt, + scheduledFor: args.scheduledFor, }, { token: tokenOf(req), requestId: req.id } );