diff --git a/scripts/project_claude_md.template b/scripts/project_claude_md.template index b0d2502..3c52ed9 100644 --- a/scripts/project_claude_md.template +++ b/scripts/project_claude_md.template @@ -1,191 +1,16 @@ -# {PROJECT_NAME} — Claude Code Instructions +# DEPRECATED — see project_rules/ -## Custodian State Hub Integration +This file was the monolithic CLAUDE.md template. It has been replaced by the +modular @-import structure in `scripts/project_rules/`. -This project is tracked as the **{DOMAIN}** domain in the Custodian State Hub. -Hub topic ID: `{TOPIC_ID}` +`register_project.sh` now generates: + - `CLAUDE.md` — thin @-import index (9 lines) + - `.claude/rules/repo-identity.md` — purpose, domain, slug, topic ID + - `.claude/rules/session-protocol.md`— orient → inbox → workplans → brief + - `.claude/rules/first-session.md` — bootstrap flow (delete once past FSP) + - `.claude/rules/workplan-convention.md` — prefix, location, delegates to global + - `.claude/rules/stack-and-commands.md` — language, deps, dev commands (stub) + - `.claude/rules/architecture.md` — design overview (stub) + - `.claude/rules/repo-boundary.md` — what this repo does NOT own (stub) -The State Hub runs locally at http://127.0.0.1:8000. The MCP server (`state-hub`) -exposes tools for reading and writing state without touching the API directly. - ---- - -### Session Protocol - -**On receiving your first message — before writing any response text — execute -this orientation sequence. Do not greet, do not ask what to do first.** - -**Step 1 — Call the State Hub** -``` -get_domain_summary("{DOMAIN}") # workstreams, blocking decisions, recent progress, SBOM status -``` -If the call fails, the API is offline: `cd ~/the-custodian/state-hub && make api` - -**Step 1b — Check the agent inbox** -``` -get_messages(to_agent="{REPO_SLUG}", unread_only=True) -``` -Mark messages read with `mark_message_read(message_id)`. Reply or act on any coordination -requests before proceeding. The hub agent uses `to_agent="hub"`; domain agents use their -repo slug as their agent name (e.g. `"{REPO_SLUG}"`). - -**Step 2 — Scan local workplans** - -Read every `.md` file under `workplans/`. Use `Glob(pattern="**/*.md", path="workplans/")` -or Bash `ls workplans/` to discover them. For each file with `status: active`, -extract and note: -- The workplan title and ID -- All tasks whose `status` is `todo` or `in_progress` - -**Step 3 — Present orientation to the user** - -Output a concise brief covering: -1. **Active workstreams** (from state hub) for the `{DOMAIN}` domain — title, - task counts, any blocking decisions -2. **Pending tasks for this repo** — from local `workplans/` files (Step 2) - plus any state hub tasks with `[repo:{REPO_SLUG}]` in their title -3. **Goal guidance** — if the summary contains a `goal_guidance` key, act on it: - - **`needs_workplan`** entries: for each active repo goal with no linked workstream, - surface it as the top suggested action — *"Repo goal '{title}' has no workplan yet. - Suggest: create workplans/{REPO_SLUG}-WP-NNNN-.md and register a workstream - with repo_goal_id='{goal_id}'"*. Treat this as higher priority than continuing - existing work unless Bernd says otherwise. - - **`alignment_warnings`** entries: if active workstreams exist but are not linked - to the current repo goal, name the most recently active one and note: - *"Current work on '{recent_workstream_title}' may not be aligned with the active - goal '{active_goal_title}'. Continue unless you hear otherwise — but flag it."* -4. **Suggested next action** — the highest-priority open item across all sources, - with goal alignment taken into account -5. **SBOM status** — is `last_sbom_at` set for this repo? If not, note it as a gap - -If there are no workstreams at all: follow the First Session Protocol below. - -**During work:** -- Use `record_decision()` for any decision that affects direction or dependencies. -- Use `add_progress_event()` for notable events (milestones, blockers, insights). -- Use `resolve_decision()` to close a decision once the choice is made. - -> **Design boundary:** The State Hub is a *read model*. Two write operations are -> permanently sanctioned: **Resolving Decisions** and **Suggesting Next Steps**. -> The bootstrap tools (`create_workstream`, `create_task`, `update_task_status`) -> are only for First Session Protocol. Formal work structure — workplans, tasks — -> belongs in the domain repo as files (ADR-001), not managed through the hub alone. - -**At the end of every session:** -- Call `add_progress_event()` with a summary of what was accomplished or decided. - Include `topic_id: {TOPIC_ID}` and the relevant `workstream_id`. - ---- - -### Repo Boundary Rule - -This agent is responsible for files **in this repo only**. - -- **Do not** write files or make commits in any other repository -- **Do not** create workplan files in other repos on their behalf -- When you identify work for another registered repo (**ecosystem todo**): - create a state hub task with `[repo:]` in the title — the other repo's - agent will see it at session start and create its own workplan -- When you identify work for an upstream repo (**third-party todo**): - create a contribution artifact in `contrib/` and register it - -Terminology and workflows: `http://localhost:3000/docs/inter-repo-communication` - ---- - -### First Session Protocol - -Triggered when `get_domain_summary("{DOMAIN}")` shows **no workstreams** for the `{DOMAIN}` -topic. The project is registered but work has not yet been structured. - -**Step 1 — Understand the project (read, don't write)** -- `~/the-custodian/canon/projects/{DOMAIN}/project_charter_v0.1.md` — purpose, scope -- `~/the-custodian/canon/projects/{DOMAIN}/roadmap_v0.1.md` — planned phases -- Scan the repo root: README, directory structure, existing code or docs - -**Step 2 — Survey in-progress work** -- Look for TODOs, open branches, half-finished files, notes -- Note what is already done vs. what is clearly started but incomplete - -**Step 3 — Propose workstreams to Bernd** -Propose 1–3 workstreams — each a coherent strand of work lasting weeks to months, -named clearly, anchored to a roadmap phase. **Wait for approval before creating.** - -**Step 4 — Create workplan file first, then DB record** -Per ADR-001, work items originate as files in the repo: -``` -workplans/-WP-NNNN-.md ← write this first -``` -Then register in the hub: -``` -create_workstream(topic_id="{TOPIC_ID}", title="...", owner="...", description="...") -create_task(workstream_id="", title="...", priority="high|medium|low") -``` - -**Step 5 — Record the setup** -``` -add_progress_event( - summary="First session: structured {DOMAIN} work into N workstreams, M tasks", - event_type="milestone", - topic_id="{TOPIC_ID}", - detail={"workstreams": [...], "tasks_created": M} -) -``` - ---- - -### Workplan Convention (ADR-001) - -Work items MUST originate as files in this repo before being registered in the hub. - -**File location:** `workplans/-.md` -**Frontmatter required:** `id`, `type: workplan`, `domain`, `repo`, `status`, -`state_hub_workstream_id`, `state_hub_task_id` (per task) - -When another domain's agent identifies work for this repo, it creates a state hub -task with `[repo:{REPO_SLUG}]` in the title (an **ecosystem todo**). You will -see it at session start via `get_domain_summary("{DOMAIN}")`. When you pick it up, create -the corresponding workplan file in `workplans/` (ADR-001) and begin work. - ---- - -### Contribution Tracking - -Track upstream contributions in `contrib/` — bug reports (BR), feature requests -(FR), extension-point proposals (EP), upstream PRs (UPR). - -``` -contrib/ - bug-reports/ # br-YYYY-MM-DD--org--repo--slug.md - feature-requests/ # fr-YYYY-MM-DD--org--repo--slug.md - extension-points/ # EP-{DOMAIN}-NNN--org--repo--slug.md - upstream-prs/ # upr-YYYY-MM-DD--org--repo--slug.md -``` - -Templates: `~/the-custodian/canon/standards/contrib-templates/` -Convention: `~/the-custodian/canon/standards/contribution-convention_v0.1.md` - -``` -register_contribution(type="br|fr|ep|upr", title="...", target_org="...", - target_repo="...", body_path="contrib/...", related_workstream_id="") -update_contribution_status(contribution_id="", status="submitted") -``` - ---- - -### SBOM - -After updating dependencies, re-ingest the SBOM: -```bash -cd ~/the-custodian/state-hub -make ingest-sbom REPO={REPO_SLUG} SCAN=1 REPO_PATH=$(pwd) -``` - -Check compliance: `http://localhost:3000/repos` -Standard: `~/the-custodian/canon/standards/sbom-convention_v0.1.md` - ---- - -### Quick Reference - -`~/the-custodian/state-hub/mcp_server/TOOLS.md` — compact MCP tool reference +See `ops-bridge/.claude/` for a complete reference example. diff --git a/scripts/project_rules/architecture.template b/scripts/project_rules/architecture.template new file mode 100644 index 0000000..4f6c74c --- /dev/null +++ b/scripts/project_rules/architecture.template @@ -0,0 +1,8 @@ +## Architecture + + + +## Quick Reference + +`~/the-custodian/state-hub/mcp_server/TOOLS.md` — MCP tool reference diff --git a/scripts/project_rules/claude-md.template b/scripts/project_rules/claude-md.template new file mode 100644 index 0000000..b80cf0d --- /dev/null +++ b/scripts/project_rules/claude-md.template @@ -0,0 +1,9 @@ +# {PROJECT_NAME} — Claude Code Instructions + +@.claude/rules/repo-identity.md +@.claude/rules/session-protocol.md +@.claude/rules/first-session.md +@.claude/rules/workplan-convention.md +@.claude/rules/stack-and-commands.md +@.claude/rules/architecture.md +@.claude/rules/repo-boundary.md diff --git a/scripts/project_rules/first-session.template b/scripts/project_rules/first-session.template new file mode 100644 index 0000000..c8b09da --- /dev/null +++ b/scripts/project_rules/first-session.template @@ -0,0 +1,38 @@ +## First Session Protocol + +Triggered when `get_domain_summary("{DOMAIN}")` shows **no workstreams**. +The project is registered but work has not yet been structured. + +**Step 1 — Read, don't write** +- `~/the-custodian/canon/projects/{DOMAIN}/project_charter_v0.1.md` — purpose, scope +- `~/the-custodian/canon/projects/{DOMAIN}/roadmap_v0.1.md` — planned phases +- Scan repo root: README, directory structure, existing code or docs + +**Step 2 — Survey in-progress work** +Look for TODOs, open branches, half-finished files. Note done vs. started but incomplete. + +**Step 3 — Propose workstreams to Bernd** +Propose 1–3 workstreams — each a coherent strand, weeks to months, anchored to a +roadmap phase. **Wait for approval before creating.** + +**Step 4 — Create workplan file first, then DB record (ADR-001)** +``` +workplans/{REPO_SLUG}-WP-NNNN-.md ← write this first +``` +Then register in the hub: +``` +create_workstream(topic_id="{TOPIC_ID}", title="...", owner="...", description="...") +create_task(workstream_id="", title="...", priority="high|medium|low") +``` + +**Step 5 — Record the setup** +``` +add_progress_event( + summary="First session: structured {DOMAIN} into N workstreams, M tasks", + event_type="milestone", + topic_id="{TOPIC_ID}", + detail={"workstreams": [...], "tasks_created": M} +) +``` + + diff --git a/scripts/project_rules/repo-boundary.template b/scripts/project_rules/repo-boundary.template new file mode 100644 index 0000000..ea4e1f5 --- /dev/null +++ b/scripts/project_rules/repo-boundary.template @@ -0,0 +1,8 @@ +## Repo boundary + +This repo owns **{PROJECT_NAME}** only. It does not own: + + diff --git a/scripts/project_rules/repo-identity.template b/scripts/project_rules/repo-identity.template new file mode 100644 index 0000000..a94bb4f --- /dev/null +++ b/scripts/project_rules/repo-identity.template @@ -0,0 +1,5 @@ +**Purpose:** {PROJECT_DESCRIPTION} + +**Domain:** {DOMAIN} +**Repo slug:** {REPO_SLUG} +**Topic ID:** {TOPIC_ID} diff --git a/scripts/project_rules/session-protocol.template b/scripts/project_rules/session-protocol.template new file mode 100644 index 0000000..503b7f2 --- /dev/null +++ b/scripts/project_rules/session-protocol.template @@ -0,0 +1,48 @@ +## Session Protocol + +State Hub: http://127.0.0.1:8000 + +**Step 1 — Orient** +``` +get_domain_summary("{DOMAIN}") +``` +If offline: `cd ~/the-custodian/state-hub && make api` + +**Step 2 — Check inbox** +``` +get_messages(to_agent="{REPO_SLUG}", unread_only=True) +``` +Mark read with `mark_message_read(message_id)`. Reply or act on coordination +requests before proceeding. + +**Step 3 — Scan workplans** +```bash +ls workplans/ +``` +For each file with `status: active`, note pending `todo`/`in_progress` tasks. + +**Step 4 — Present brief** + +1. **Active workstreams** for `{DOMAIN}` — title, task counts, blocking decisions +2. **Pending tasks** from `workplans/` + any `[repo:{REPO_SLUG}]` hub tasks +3. **Goal guidance** — if `goal_guidance` in summary: + - `needs_workplan`: surface as top action — *"Repo goal '{title}' has no workplan yet"* + - `alignment_warnings`: flag if active work is not aligned with current goal +4. **Suggested next action** — highest-priority open item +5. **SBOM status** — flag if `last_sbom_at` is unset for this repo + +If no workstreams: follow First Session Protocol (`first-session.md`). + +**During work:** `record_decision()` · `add_progress_event()` · `resolve_decision()` + +> State Hub is a *read model*. Bootstrap tools (`create_workstream`, `create_task`) +> are First Session Protocol only. Work structure belongs in repo files (ADR-001). + +**Session close:** +``` +add_progress_event(summary="...", topic_id="{TOPIC_ID}", workstream_id="") +``` +If workplan files were modified: +```bash +cd ~/the-custodian/state-hub && make fix-consistency REPO={REPO_SLUG} +``` diff --git a/scripts/project_rules/stack-and-commands.template b/scripts/project_rules/stack-and-commands.template new file mode 100644 index 0000000..dc53ac6 --- /dev/null +++ b/scripts/project_rules/stack-and-commands.template @@ -0,0 +1,19 @@ +## Stack + + +- **Language:** +- **Key deps:** + +## Dev Commands + +```bash +# TODO: Fill in the standard commands for this repo + +# Install dependencies + +# Run tests + +# Lint / type check + +# Build / package (if applicable) +``` diff --git a/scripts/project_rules/workplan-convention.template b/scripts/project_rules/workplan-convention.template new file mode 100644 index 0000000..d0754e7 --- /dev/null +++ b/scripts/project_rules/workplan-convention.template @@ -0,0 +1,12 @@ +## Workplan Convention (ADR-001) + +File location: `workplans/{REPO_SLUG}-WP-NNNN-.md` +ID prefix: `{WP_PREFIX}` + +Work items originate as files in this repo **before** being registered in the hub. + +Ecosystem todos from other agents arrive as `[repo:{REPO_SLUG}]` hub tasks — +visible at session start. Pick one up by creating the workplan file, then registering +the workstream. + + diff --git a/scripts/register_project.sh b/scripts/register_project.sh index 45f06b3..0f7fe21 100755 --- a/scripts/register_project.sh +++ b/scripts/register_project.sh @@ -4,7 +4,7 @@ # Usage: scripts/register_project.sh [--additional] # domain: slug of an active domain (e.g. custodian, railiance) # project_path: absolute path to the project directory -# --additional: add a second repo to an existing domain; skip CLAUDE.md +# --additional: add a second repo to an existing domain; skip CLAUDE.md generation # # Example: # scripts/register_project.sh railiance /home/worsch/railiance @@ -15,9 +15,12 @@ # 2. Verify the domain exists via GET /domains/{slug}/ # 3. Look up the topic ID for the domain (first active topic) # 4. Check that state-hub is in ~/.claude.json; warn if missing -# 5. Write $project_path/CLAUDE.custodian.md (suggestion; repo agent integrates → CLAUDE.md) +# 5. Write CLAUDE.md + .claude/rules/*.md (modular @-import structure) +# - If CLAUDE.md already exists, writes .claude/rules/ only and appends +# an @-import comment block to the existing CLAUDE.md # 6. POST to /repos/ to register the repo -# 7. POST a progress event recording the registration +# 7. POST /repos/{slug}/paths/ to register this machine's local path (host_paths) +# 8. POST a progress event recording the registration set -euo pipefail @@ -26,6 +29,7 @@ PROJECT_PATH="${2:-}" ADDITIONAL="${3:-}" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" STATE_HUB_DIR="$(dirname "$SCRIPT_DIR")" +RULES_TEMPLATES_DIR="$SCRIPT_DIR/project_rules" API_BASE="${API_BASE:-http://127.0.0.1:8000}" # ── Validate args ────────────────────────────────────────────────────────────── @@ -45,6 +49,10 @@ fi PROJECT_NAME="$(basename "$PROJECT_PATH")" REPO_SLUG="$(echo "$PROJECT_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-\|-$//g')" +# Derive a workplan prefix: uppercase first token + -WP (e.g. marki-docx → MARKI-WP) +FIRST_TOKEN="$(echo "$REPO_SLUG" | cut -d'-' -f1 | tr '[:lower:]' '[:upper:]')" +WP_PREFIX="${FIRST_TOKEN}-WP" + # ── Step 1: API health check ─────────────────────────────────────────────────── echo "==> Checking API at $API_BASE ..." if ! curl -sf "$API_BASE/state/health" > /dev/null; then @@ -58,33 +66,27 @@ echo " API OK" # ── Step 2: Verify domain exists ─────────────────────────────────────────────── echo "==> Verifying domain '$DOMAIN' ..." DOMAIN_JSON="$(curl -sf "$API_BASE/domains/$DOMAIN/" 2>/dev/null || echo 'NOT_FOUND')" -if [[ "$DOMAIN_JSON" == "NOT_FOUND" ]] || echo "$DOMAIN_JSON" | python3 -c "import json,sys; d=json.load(sys.stdin); sys.exit(0 if d.get('slug') else 1)" 2>/dev/null; then - if [[ "$DOMAIN_JSON" == "NOT_FOUND" ]] || ! echo "$DOMAIN_JSON" | python3 -c "import json,sys; d=json.load(sys.stdin); sys.exit(0 if d.get('slug') else 1)" 2>/dev/null; then - echo "ERROR: Domain '$DOMAIN' not found in the State Hub." - echo " To create: make add-domain DOMAIN=$DOMAIN NAME=\"\"" - echo " To list available: curl -s $API_BASE/domains/ | python3 -m json.tool" - exit 1 - fi +if [[ "$DOMAIN_JSON" == "NOT_FOUND" ]] || ! echo "$DOMAIN_JSON" | python3 -c "import json,sys; d=json.load(sys.stdin); sys.exit(0 if d.get('slug') else 1)" 2>/dev/null; then + echo "ERROR: Domain '$DOMAIN' not found in the State Hub." + echo " To create: make add-domain DOMAIN=$DOMAIN NAME=\"\"" + echo " To list available: curl -s $API_BASE/domains/ | python3 -m json.tool" + exit 1 fi echo " Domain OK" # ── Step 3: Look up topic ID ─────────────────────────────────────────────────── echo "==> Looking up topic for domain '$DOMAIN' ..." TOPICS_JSON="$(curl -sf "$API_BASE/topics/?status=active")" - TOPIC_ID="$(echo "$TOPICS_JSON" | python3 -c " import json, sys topics = json.load(sys.stdin) match = next((t for t in topics if t.get('domain_slug') == sys.argv[1]), None) -if not match: - print('NOT_FOUND') -else: - print(match['id']) +print(match['id'] if match else 'NOT_FOUND') " "$DOMAIN")" if [[ "$TOPIC_ID" == "NOT_FOUND" ]]; then - echo "WARNING: No active topic found for domain '$DOMAIN'. CLAUDE.md will omit topic_id." - TOPIC_ID="" + echo "WARNING: No active topic found for domain '$DOMAIN'. repo-identity.md will omit topic_id." + TOPIC_ID="(none)" else echo " topic_id: $TOPIC_ID" fi @@ -99,124 +101,165 @@ if not f.exists(): print('MISSING_FILE') else: d = json.loads(f.read_text()) - servers = d.get('mcpServers', {}) - print('OK' if 'state-hub' in servers else 'NOT_REGISTERED') + print('OK' if 'state-hub' in d.get('mcpServers', {}) else 'NOT_REGISTERED') ")" +case "$MCP_OK" in + MISSING_FILE) echo "WARNING: ~/.claude.json not found. MCP server not registered." ;; + NOT_REGISTERED) echo "WARNING: 'state-hub' not in ~/.claude.json. See global CLAUDE.md §MCP Server Registration." ;; + *) echo " MCP OK" ;; +esac -if [[ "$MCP_OK" == "MISSING_FILE" ]]; then - echo "WARNING: ~/.claude.json not found. MCP server is not registered." -elif [[ "$MCP_OK" == "NOT_REGISTERED" ]]; then - echo "WARNING: 'state-hub' not found in ~/.claude.json." - echo " To register, see CLAUDE.md MCP Server Registration section." -else - echo " MCP OK" -fi - -# ── Step 5: Write CLAUDE.custodian.md (suggestion for the repo agent) ───────── -CLAUDE_MD="$PROJECT_PATH/CLAUDE.md" -SUGGESTION_FILE="$PROJECT_PATH/CLAUDE.custodian.md" -TEMPLATE="$SCRIPT_DIR/project_claude_md.template" - -echo "==> Writing custodian suggestion to $SUGGESTION_FILE ..." -{ - cat <<'PREAMBLE' - - -PREAMBLE +# ── Helper: render a template with variable substitution ────────────────────── +render_template() { + local tmpl="$1" sed \ -e "s|{PROJECT_NAME}|$PROJECT_NAME|g" \ + -e "s|{PROJECT_DESCRIPTION}|$PROJECT_NAME — (fill in purpose)|g" \ -e "s|{DOMAIN}|$DOMAIN|g" \ -e "s|{TOPIC_ID}|$TOPIC_ID|g" \ -e "s|{REPO_SLUG}|$REPO_SLUG|g" \ - "$TEMPLATE" -} > "$SUGGESTION_FILE" -echo " Written. The repo agent should integrate it into CLAUDE.md and delete this file." + -e "s|{WP_PREFIX}|$WP_PREFIX|g" \ + "$tmpl" +} + +# ── Step 5: Write .claude/rules/ + CLAUDE.md ────────────────────────────────── +if [[ "$ADDITIONAL" != "--additional" ]]; then + RULES_DIR="$PROJECT_PATH/.claude/rules" + CLAUDE_MD="$PROJECT_PATH/CLAUDE.md" + + echo "==> Writing .claude/rules/ ..." + mkdir -p "$RULES_DIR" + + for rule in repo-identity session-protocol first-session workplan-convention \ + stack-and-commands architecture repo-boundary; do + tmpl="$RULES_TEMPLATES_DIR/${rule}.template" + out="$RULES_DIR/${rule}.md" + if [[ -f "$tmpl" ]]; then + render_template "$tmpl" > "$out" + echo " .claude/rules/${rule}.md" + else + echo "WARNING: template missing: $tmpl" + fi + done + + if [[ -f "$CLAUDE_MD" ]]; then + echo "" + echo "==> CLAUDE.md already exists — appending @-import suggestion." + cat >> "$CLAUDE_MD" << 'SUGGESTION' + + +SUGGESTION + echo " Suggestion appended to existing CLAUDE.md." + else + echo "==> Writing CLAUDE.md ..." + render_template "$RULES_TEMPLATES_DIR/claude-md.template" > "$CLAUDE_MD" + echo " CLAUDE.md written." + fi + + echo "" + echo "Rule files needing manual content:" + echo " .claude/rules/repo-identity.md — update purpose description" + echo " .claude/rules/stack-and-commands.md — language, deps, dev commands" + echo " .claude/rules/architecture.md — design overview" + echo " .claude/rules/repo-boundary.md — what this repo does NOT own" + echo " .claude/rules/workplan-convention.md — verify WP prefix: $WP_PREFIX" +fi # ── Step 6: Register repo in State Hub ──────────────────────────────────────── +echo "" echo "==> Registering repo '$PROJECT_NAME' under domain '$DOMAIN' ..." REPO_PAYLOAD="$(python3 -c " import json -payload = { +print(json.dumps({ 'domain_slug': '$DOMAIN', 'slug': '$REPO_SLUG', 'name': '$PROJECT_NAME', 'local_path': '$PROJECT_PATH', -} -print(json.dumps(payload)) +})) ")" REPO_RESULT="$(curl -sf -X POST "$API_BASE/repos/" \ -H "Content-Type: application/json" \ - -d "$REPO_PAYLOAD" 2>/dev/null || echo 'REPO_EXISTS')" + -d "$REPO_PAYLOAD" 2>/dev/null || echo 'CONFLICT')" -if [[ "$REPO_RESULT" == "REPO_EXISTS" ]]; then - echo " Repo '$REPO_SLUG' already registered (or slug conflict) — skipping." +if [[ "$REPO_RESULT" == "CONFLICT" ]]; then + echo " Repo '$REPO_SLUG' already registered — skipping POST." else echo " Repo registered: $REPO_SLUG" fi -# ── Step 7: Record progress event ───────────────────────────────────────────── +# ── Step 7: Register this machine's local path ──────────────────────────────── +echo "==> Registering host path for $(hostname) ..." +curl -sf -X POST "$API_BASE/repos/$REPO_SLUG/paths/" \ + -H "Content-Type: application/json" \ + -d "{\"host\": \"$(hostname)\", \"path\": \"$PROJECT_PATH\"}" > /dev/null \ + && echo " host_paths[$(hostname)] = $PROJECT_PATH" + +# ── Step 8: Record progress event ───────────────────────────────────────────── echo "==> Recording registration event ..." -EVENT_JSON="$(python3 -c " -import json +python3 - < /dev/null - +$([ "$TOPIC_ID" != "(none)" ] && echo "payload['topic_id'] = '$TOPIC_ID'" || echo "") +req = urllib.request.Request( + '$API_BASE/progress/', + data=json.dumps(payload).encode(), + headers={'Content-Type': 'application/json'}, + method='POST', +) +urllib.request.urlopen(req) +PYEOF echo " Event recorded." + echo "" echo "Registration complete!" echo " Project: $PROJECT_NAME" echo " Domain: $DOMAIN" echo " Repo slug: $REPO_SLUG" -[[ -n "$TOPIC_ID" ]] && echo " Topic ID: $TOPIC_ID" -echo " Suggestion: $SUGGESTION_FILE (repo agent should integrate → CLAUDE.md)" +[[ "$TOPIC_ID" != "(none)" ]] && echo " Topic ID: $TOPIC_ID" +echo " WP prefix: $WP_PREFIX (update .claude/rules/workplan-convention.md if wrong)" echo "" -echo "Next: restart Claude Code for the MCP server to be available in this project." +echo "Next steps:" +echo " 1. Fill in TODO stubs in .claude/rules/" +echo " 2. Restart Claude Code for the MCP server to be available in this project" # ── Optional: SBOM ingest ───────────────────────────────────────────────────── if [[ "$ADDITIONAL" != "--additional" ]]; then echo "" - read -r -p "==> Run SBOM ingest now? (auto-detects lockfile in $PROJECT_PATH) [y/N] " INGEST_NOW Run SBOM ingest now? [y/N] " INGEST_NOW Ingesting SBOM for '$REPO_SLUG' ..." - INGEST_UV="$STATE_HUB_DIR/.venv/bin/python" - if [[ -x "$INGEST_UV" ]]; then - "$INGEST_UV" "$SCRIPT_DIR/ingest_sbom.py" \ + INGEST_PY="$STATE_HUB_DIR/.venv/bin/python" + if [[ -x "$INGEST_PY" ]]; then + "$INGEST_PY" "$SCRIPT_DIR/ingest_sbom.py" \ --repo "$REPO_SLUG" \ --repo-path "$PROJECT_PATH" \ --api-base "$API_BASE" && echo " SBOM ingested." || echo " SBOM ingest failed (non-fatal)." else - echo " Skipping: .venv not found. Run 'make install' first, then 'make ingest-sbom REPO=$REPO_SLUG'." + echo " Skipping: .venv not found. Run 'make install' then 'make ingest-sbom REPO=$REPO_SLUG'." fi fi fi