# /wiki-compile — Compile session logs and Syncro data into wiki articles Seed new wiki articles or refresh existing ones from session logs, client documents, and live Syncro PSA data. --- ## Usage ``` /wiki-compile client: Seed or refresh a client wiki article /wiki-compile client: --full Force full recompile of existing article (Sonnet synthesis) /wiki-compile project: Compile a project wiki article (no Syncro) /wiki-compile system: Compile a system wiki article (no Syncro) /wiki-compile all Process all missing + stale articles ``` **Mode auto-detection:** - If `wiki/clients/.md` **does not exist** → **Seed mode** (full synthesis, Sonnet subagent) - If `wiki/clients/.md` **exists** and no `--full` flag → **Refresh mode** (surgical update of dynamic fields only, no subagent) - `--full` flag → **Full recompile** (Sonnet synthesis, preserves existing Patterns/History) --- ## Phase 0 — Setup ```bash CLAUDETOOLS_ROOT="D:/claudetools" VAULT="$CLAUDETOOLS_ROOT/.claude/scripts/vault.sh" # Syncro auth (read-only operations only — GET requests ONLY in this skill) BASE="https://computerguru.syncromsp.com/api/v1" USER_ID=$(jq -r '.user // empty' "$CLAUDETOOLS_ROOT/.claude/identity.json") case "$USER_ID" in mike) API_KEY="T259810e5c9917386b-52c2aeea7cdb5ff41c6685a73cebbeb3" ;; howard) API_KEY="Tde5174a6e9e312d14-02fd5bfe0f0ee40c87d027507c680e18" ;; *) echo "[WARNING] Unknown user — Syncro enrichment skipped" ; API_KEY="" ;; esac ``` **Synthesis engine:** seed/full article drafting is done by a **Sonnet subagent** (Agent tool, `model: "sonnet"`), not Ollama. The main agent gathers sources + Syncro data, delegates the draft, then reviews it before writing. No Ollama dependency. --- ## Phase 1 — Argument Parsing Parse the target and flags: ```bash # Extract type and slug from argument like "client:cascades-tucson" or "client:cascades-tucson --full" TARGET_RAW="$1" # e.g. "client:cascades-tucson" FULL_FLAG="${2:-}" # "--full" or empty TARGET_TYPE="${TARGET_RAW%%:*}" # client | project | system | all SLUG="${TARGET_RAW#*:}" # cascades-tucson # Determine article path case "$TARGET_TYPE" in client) ARTICLE_PATH="wiki/clients/${SLUG}.md" ;; project) ARTICLE_PATH="wiki/projects/${SLUG}.md" ;; system) ARTICLE_PATH="wiki/systems/${SLUG}.md" ;; all) # handled separately — see "all" mode below esac # Mode detection if [ ! -f "$CLAUDETOOLS_ROOT/$ARTICLE_PATH" ]; then MODE="seed" elif [ "$FULL_FLAG" = "--full" ]; then MODE="full" else MODE="refresh" fi echo "[INFO] Mode: $MODE | Target: $TARGET_TYPE:$SLUG" ``` --- ## Phase 2 — Syncro Enrichment (clients only, skip for project/system) **Skip this phase entirely if `TARGET_TYPE != client` or `API_KEY` is empty.** ### 2a — Customer Search Convert slug to a Syncro search query: ```bash # Replace hyphens with spaces for the search query SEARCH_QUERY=$(echo "$SLUG" | sed 's/-/ /g') CUST_RESULTS=$(curl -s "$BASE/customers?name=$(python3 -c "import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1]))" "$SEARCH_QUERY")&per_page=5&api_key=$API_KEY") CUST_COUNT=$(echo "$CUST_RESULTS" | jq '.customers | length') ``` **If 0 results:** ``` [SYNCRO] No customer found matching '${SEARCH_QUERY}' — skipping Syncro enrichment. Proceeding with session logs only. ``` Set `SYNCRO_DATA=""` and continue to Phase 3. **If 2+ results:** Show the list and PAUSE execution: ``` [SYNCRO] Multiple customers match '${SEARCH_QUERY}': 1. () 2. () ... Which customer should be used for this wiki article? (Enter number, or 'skip' to skip Syncro) ``` Wait for user input before continuing. If user says `skip`, treat as 0 results. **If exactly 1 result:** Proceed immediately. ### 2b — Customer Profile Pull ```bash CUST_ID=$(echo "$CUST_RESULTS" | jq -r '.customers[0].id') # Full customer record CUST=$(curl -s "$BASE/customers/${CUST_ID}?api_key=$API_KEY" | jq '.customer') DISPLAY_NAME=$(echo "$CUST" | jq -r '.business_name // .firstname + " " + .lastname') PREPAY_HOURS=$(echo "$CUST" | jq -r '.prepay_hours // "0"') NOTES=$(echo "$CUST" | jq -r '.notes // ""') # Contacts: name, title, email, phone CONTACTS=$(echo "$CUST" | jq -r ' .contacts[]? | "\(.firstname) \(.lastname)" + (if .title != "" and .title != null then " (\(.title))" else "" end) + (if .email != "" and .email != null then " — \(.email)" else "" end) + (if .mobile != "" and .mobile != null then ", \(.mobile)" elif .phone != "" and .phone != null then ", \(.phone)" else "" end) ') ``` ### 2c — Open Tickets ```bash OPEN_TICKETS=$(curl -s "$BASE/tickets?customer_id=${CUST_ID}&status=New,In+Progress,Scheduled,Waiting+on+Customer&per_page=10&api_key=$API_KEY" | jq '.tickets[]? | {id, number, subject, status, created_at, user_id}') TICKET_COUNT=$(echo "$OPEN_TICKETS" | jq -s 'length') ``` ### 2d — Recent Invoices (last 12) ```bash RECENT_INVOICES=$(curl -s "$BASE/invoices?customer_id=${CUST_ID}&per_page=12&api_key=$API_KEY" | jq '[.invoices[]? | {id, number, date, total, status}]') ``` Used only to infer billing pattern (break-fix vs prepaid, rate hints). Do not expose raw invoice data in the wiki. ### 2e — Asset Count ```bash ASSET_COUNT=$(curl -s "$BASE/customer_assets?customer_id=${CUST_ID}&per_page=200&api_key=$API_KEY" | jq '[.assets[]?] | length') ``` Only the count is used — individual asset details go in session logs and client docs, not the wiki. --- ## Phase 2P — Authoritative Artifact Discovery (projects only) **Applies when `TARGET_TYPE == project`.** Skip for clients/systems. Session logs narrate *work done and why* — they are a structurally incomplete record of *what a product can do*. For a code-bearing project, the authoritative capability record is the **code, migrations, API routes, and roadmap**, not the logs. Compiling a project from logs alone WILL miss shipped features (this is exactly how the GuruRMM `user_session` command context was missed). So for projects, dig into the artifacts directly. ### 2P-a — Locate the repo and guard against a stale submodule Many projects are tracked as a **pinned git submodule** whose commit deliberately lags the live repo. Reading the working tree alone gives stale artifacts. Always check: ```bash REPO="$CLAUDETOOLS_ROOT/projects/" cd "$REPO" git fetch origin main 2>/dev/null PINNED=$(git rev-parse --short HEAD) LIVE=$(git rev-parse --short origin/main) echo "pinned=$PINNED live=$LIVE" # If they differ, read artifacts from origin/main, NOT the working tree. REF="origin/main" # use this ref for all artifact reads below; falls back to HEAD if no remote ``` Read live artifacts without disturbing the pinned pointer, either via `git show $REF:` / `git ls-tree -r --name-only $REF -- `, or by creating a throwaway worktree: `git worktree add /tmp/-live $REF` (remove with `git worktree remove` when done). ### 2P-b — Gather authoritative artifacts (priority order) 1. **DB migrations** — `git ls-tree -r --name-only $REF -- `. Each migration is a feature/schema checkpoint; the filenames alone are a capability timeline (e.g. `041_add_command_context`). This is the most reliable signal and is usually current even when changelogs are not. 2. **API routes** — the real server surface. Read the route-registration file(s) (e.g. `server/src/api/mod.rs`) and the per-resource handler modules. Enumerate endpoints + notable request options (auth modes, contexts, scopes). 3. **Agent / client capabilities** — module tree of the agent/client (e.g. `agent/src/`): metrics, checks, command execution, updater, tunnel, watchdog, inventory, registry ops. Note **per-platform coverage**. 4. **Completed roadmap items** — `docs/FEATURE_ROADMAP.md` checked/done items. 5. **Specs** — `docs/specs/` (shipped vs proposed). 6. **Commit log** — `git log --oneline $REF` filtered for `feat`/`perf` since the article's `last_compiled`. Fuller and more current than changelogs. 7. **Changelogs** — read if present, but treat as **incomplete** (they are frequently stale — verified on GuruRMM: committed changelogs stopped at v0.6.22 while the fleet ran 0.6.39+). Never rely on them as the sole capability source. 8. **README / DESIGN / ARCHITECTURE docs** — for framing and locked decisions. ### 2P-c — Synthesize the Capabilities / Feature Set section From the artifacts above, produce the **Capabilities / Feature Set** section (see project template). Organize by surface (monitoring, remote execution, management, integrations, security). Explicitly capture **execution modes and important options** — e.g. command contexts (`system` vs `user_session`), auth modes, policy scopes, platform coverage. Cross-check the existing article (full recompile) and **correct any capability statement that is now incomplete or wrong** (e.g. "runs as LocalSystem" without the user-session context). For large repos, delegate the artifact read + synthesis to an agent (general-purpose) pointed at the live ref/worktree, and integrate the returned section after review — don't flood the main context with the full code/migration dump. --- ## Phase 3 — Session Log Discovery Find all session logs that mention this client: ```bash cd "$CLAUDETOOLS_ROOT" # 1. Client-specific session logs (all) CLIENT_LOGS=$(find "clients/${SLUG}/session-logs/" -name "*.md" 2>/dev/null | sort) # 2. Client-specific docs (README, CONTEXT.md, overview.md, etc.) CLIENT_DOCS=$(find "clients/${SLUG}/" -name "*.md" -not -path "*/session-logs/*" 2>/dev/null | sort) # 3. Root session logs mentioning this client (case-insensitive grep on slug and display name) ROOT_LOGS=$(grep -ril "$SLUG\|$(echo "$DISPLAY_NAME" | sed 's/ /\\|/g')" session-logs/*.md 2>/dev/null | sort) # 4. Memory files referencing this client MEMORY_FILES=$(grep -ril "$SLUG" .claude/memory/*.md 2>/dev/null | sort) # Deduplicate and collect all source paths ALL_SOURCES=$(echo "$CLIENT_LOGS $CLIENT_DOCS $ROOT_LOGS $MEMORY_FILES" | tr ' ' '\n' | sort -u | grep -v '^$') SOURCE_COUNT=$(echo "$ALL_SOURCES" | grep -c '^' || echo 0) echo "[INFO] Found $SOURCE_COUNT source files" ``` If `SOURCE_COUNT == 0` and no Syncro data: warn and stop. ``` [ERROR] No session logs and no Syncro data found for '${SLUG}'. Cannot compile. Create at least one session log in clients/${SLUG}/session-logs/ first. ``` --- ## Phase 4 — Article Generation ### Refresh Mode (existing article, no --full) Perform surgical updates only. No Ollama call. Three edits: **Edit 1 — Update hours remaining in Profile section:** Find the `Hours remaining` line and replace with live Syncro value and today's date. Only run if `PREPAY_HOURS` is non-null and non-zero OR if the article currently shows a non-zero balance. ``` - **Hours remaining (if prepaid):** ${PREPAY_HOURS} hrs as of $(date +%Y-%m-%d) ``` **Edit 2 — Update Active Work ticket list:** Replace the content of `## Active Work` with the Syncro open tickets formatted as: ```markdown ## Active Work *As of $(date +%Y-%m-%d) — Syncro shows ${TICKET_COUNT} open ticket(s):* | Ticket | Subject | Status | Opened | |---|---|---|---| | # (ID: ) | | | | ``` If `TICKET_COUNT == 0`: ```markdown ## Active Work *No open tickets in Syncro as of $(date +%Y-%m-%d). See session logs for recent work.* ``` **Edit 3 — Update frontmatter:** - `last_compiled`: today's date - `compiled_by`: `/claude-main` - Append new source files to `sources:` list (deduplicate) After edits, emit: ``` [OK] Refresh complete for wiki/clients/.md - Hours: updated to ${PREPAY_HOURS} hrs - Active tickets: ${TICKET_COUNT} open - Sources: ${SOURCE_COUNT} files tracked ``` ### Seed Mode / Full Recompile — Claude Synthesis (Sonnet subagent) Prepare the synthesis context by reading the most relevant source files. For session logs, read the full content of client-specific logs and the first 200 lines of root session logs (to avoid overwhelming the prompt). For full recompile, also read the existing article. Delegate the draft to a **Sonnet subagent** via the Agent tool (`model: "sonnet"`), passing the brief below. The subagent returns the article markdown; the main agent reviews it (billing/IPs/vault-paths accurate, Patterns/History preserved on full recompile) before writing in Phase 5. **Subagent brief:** ``` You are compiling a wiki article for an MSP (managed service provider) client. Produce a structured Markdown article using the template and source data provided. Be concise, factual, and technical. No filler phrases. No emojis. Past tense for history. Mark unknown fields as (verify). --- CLIENT SLUG: DISPLAY NAME: COMPILE MODE: SYNCRO LIVE DATA: Customer ID: Prepaid Hours: Asset Count: Open Tickets (): Contacts: Billing pattern from invoices: SESSION LOG EXCERPTS: EXISTING ARTICLE (preserve Patterns and History, update everything else): --- TEMPLATE STRUCTURE TO FOLLOW: --- RULES: 1. BILLING — Syncro is the authoritative source for ALL billing-related fields. Never use session log values for these: - Hours remaining: use live `prepay_hours` from Syncro customer record - Contract type: if `prepay_hours > 0` → "Prepaid hour block"; if recent invoices show per-ticket billing → "Break-fix"; if large flat invoices → "Project" - Billing rate: use the `price_retail` from the most recent non-zero labor line item in recent invoices; if no invoices yet, write "(verify — check Syncro invoices)" - Customer ID: use Syncro `id` exactly as returned - Managed device count: use Syncro asset count 2. Infrastructure: derive from session logs; keep Syncro asset count in Profile, not in infrastructure tables 3. Patterns & Known Issues: synthesize from session logs; for full recompile preserve existing patterns verbatim unless session logs show they are resolved 4. Active Work: use Syncro open ticket list as the primary source 5. History Highlights: chronological, from session logs only, one-line entries with dates 6. Access: vault paths and IPs from session logs; never invent vault paths 6b. NEVER inline raw secrets (passwords, PSKs, RADIUS/shared secrets, API keys, PFX passwords) into the article, even when a session log exposes them. The wiki references the vault path only — e.g. `sysadmin (password: vault)` or `secret in vault (clients//server.sops.yaml)`. Raw secrets live in session logs and the SOPS vault, never in the wiki knowledge layer. (Exception: a value the EXISTING article already discloses may be carried over to match its disclosure level — do not ADD new ones.) 7. For fields with no source data: write "(verify)" not placeholder text 8. Backlinks: list any wiki article slugs (clients/projects/systems) that this client is cross-referenced with ``` If the subagent is unavailable, the main agent writes the article directly using the same rules and all collected data. --- ## Phase 5 — Write Article + Update Index **Write the article:** - Seed: write `wiki/clients/.md` from generated content - Full: overwrite `wiki/clients/.md` - Refresh: edits already applied in Phase 4 **Update `wiki/index.md`:** - Check if `wiki/clients/.md` is listed in the Clients table - If **not listed**: insert a new row in the Clients table: ``` | [](clients/.md) | | | ``` - If **listed**: update the `Last Compiled` date and summary - Update the `Last updated` header date --- ## Phase 6 — Commit ```bash cd "$CLAUDETOOLS_ROOT" git add "wiki/clients/${SLUG}.md" wiki/index.md git commit -m "wiki: compile ${SLUG} (${MODE})" git push origin main ``` Emit: ``` [SUCCESS] wiki/clients/.md committed and pushed Mode: Sources: session logs + Syncro (customer ) Article: wiki/clients/.md Index: wiki/index.md updated ``` --- ## "all" Mode When invoked as `/wiki-compile all`: 1. Run the same checks as `/wiki-lint` Steps 1–2 to find missing and stale articles 2. For each missing article: run seed mode 3. For stale articles (compiled > 90 days ago with newer logs): run refresh mode 4. For each: pause after Syncro ambiguity check if needed — do not bulk-skip 5. After all articles processed: single commit with message `wiki: bulk compile (N articles)` --- ## Hard Rules - **This skill is read-only against Syncro.** No POST, PUT, PATCH, or DELETE. GET requests only. - **Syncro is authoritative for all billing fields.** Hours remaining, billing rate, contract type, customer ID, and asset count always come from Syncro live data — never from session logs. Session logs may be stale; Syncro is not. - **Never invent vault paths.** If a credential is not mentioned in session logs, write "(verify)" in the Access section. - **Never populate Infrastructure tables with placeholder rows.** Only include servers/services that appear in session logs or Syncro assets. - **Syncro contacts are ground truth for the Profile section.** Do not override with session log guesses if the contact name differs. - **Refresh mode never touches Patterns or History.** Those sections require human review or `--full`.