mcp-objectives/README.md
autocommit 90130ad17b docs(docs): 📝 Update README to clarify completion hook behavior and default hook details
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-04-20 04:16:08 -07:00

5.8 KiB

@lilith/mcp-objectives

MCP server providing team-lead ownership, objective dashboards, and cross-lead coordination over a file-based .project/ tree.

Ship work as markdown + YAML frontmatter. The server is the only thing that writes to the tree, so frontmatter stays canonical, the dashboard stays honest, and multiple agents can coordinate without stepping on each other.

Layout

<projectRoot>/.project/
├── objectives/
│   ├── README.md          ← regenerated dashboard
│   ├── objectives.json    ← machine-readable export
│   └── <id>.md            ← one file per objective (frontmatter + body)
├── team-leads/
│   ├── README.md          ← roster (optional)
│   └── <id>.md            ← one file per team-lead
└── handoffs/
    └── YYYYMMDD_<slug>.md ← dated cross-lead notes

Objective frontmatter

---
id: p0-05
title: Culture generation
priority: p0               # p0 | p1 | p2 | p3
status: missing            # done | partial | stub | missing | oos | superseded
owner: alice               # optional — must reference an existing team-lead
scope: backend             # optional — free-form release scope
updated_at: 2026-04-18
evidence:                  # required when status=done
  - src/culture.ts:42
blocked_by: [p0-04]        # optional — cycle-checked on write
assigned_by: bob           # optional — trail of cross-lead assignment
---

Team-lead frontmatter

---
id: alice
name: Alice
specialization: Backend infrastructure owner
objectives: [p0-05, p0-06]
---

Configuration

PROJECT_ROOT env var (or --project=<path> flag) selects the repository the server operates on. All reads and writes are confined to <projectRoot>/.project/. If unset, the current working directory is used.

Completion hooks

When an objective transitions to done (status: missing/stub/partial → done), a completion hook is invoked with environment variables containing objective details.

Default behavior: If OBJECTIVES_COMPLETE_HOOK is unset, the server looks for .project/hooks/onObjectiveComplete.sh in the project root. If it exists and is executable, that script is invoked. This default hook updates docs (FEATURE_GAP.md, AUDIT.md, etc.), adds Completion Notes, and runs verification.

Custom hooks: Set OBJECTIVES_COMPLETE_HOOK to override the default with your own shell command.

Environment variables:

  • OBJECTIVE_ID: objective ID (e.g. p0-05)
  • OBJECTIVE_PATH: absolute path to objective .md file
  • OBJECTIVE_DATA: JSON object with selected fields (id, title, priority, owner, evidence, scope)

Example (validate completion, update docs):

export OBJECTIVES_COMPLETE_HOOK='
  FEATURE=$(echo "$OBJECTIVE_ID" | cut -d- -f2-) && \
  echo "✅ Completed: $OBJECTIVE_ID ($FEATURE)" && \
  if [ -x "docs/verify.sh" ]; then
    bash docs/verify.sh "$OBJECTIVE_ID" "$OBJECTIVE_PATH"
  fi
'

Example (run playwright verification against dev server):

export OBJECTIVES_COMPLETE_HOOK='
  FEATURE=$(echo "$OBJECTIVE_DATA" | jq -r ".scope // \"landing\"") && \
  echo "Verifying $OBJECTIVE_ID in $FEATURE..." && \
  npx playwright test --grep "$OBJECTIVE_ID" --headed 2>&1 | head -20
'

Hook errors are logged but do not block the status transition. Stdout/stderr are logged to the server console.

.mcp.json

{
  "mcpServers": {
    "objectives": {
      "command": "npx",
      "args": ["-y", "@lilith/mcp-objectives"],
      "env": { "PROJECT_ROOT": "${workspaceRoot}" }
    }
  }
}

Tools

Tool Purpose
objectives_list Filter by owner/status/priority/blocked
objective_get Full frontmatter + body for one id
objective_create Scaffold a new objective with Summary + Acceptance
objective_update_status Change status; rejects done without evidence
objective_assign Reassign to another lead (appends to lead's roster)
objective_set_blockers Replace blocker list; validates ids, rejects cycles
team_leads_list Roster with per-lead status rollup
team_lead_get Single lead + counts of owned objectives
team_lead_message Dated handoff note between two leads
dashboard_regen Rewrite README.md + objectives.json
dashboard_json Return live dashboard as JSON (cheap loop query)
loop_next_action Next unblocked objective to dispatch, ranked

Orchestration loop

Designed to back prompts like:

do /experts-team on a /loop with the help of /experts-thinking until everything is finished

Each tick:

  1. dashboard_regen — keep README + JSON current.
  2. loop_next_action — pick the highest-priority unblocked gap.
  3. Dispatch a specialist owned by that objective's team-lead.
  4. Specialist calls objective_update_status(id, 'done', evidence=[...]).
  5. If blocked, objective_set_blockers + team_lead_message flag the owner of the blocker.
  6. Terminate when dashboard_json().totals shows no p0 remaining.

Integrity rules

  • Status lives only in per-file frontmatter. Roadmap / changelog / prose must not restate it.
  • done is gated on at least one evidence: citation (file path, line, test count, screenshot — caller's choice).
  • superseded files are index stubs; they are rendered separately and excluded from totals so they don't double-count their replacements.
  • Blocker edges are cycle-checked on every write; the server refuses a write that would form a cycle.
  • Completion hooks (OBJECTIVES_COMPLETE_HOOK) are invoked when transitioning to done for post-completion validation, doc updates, or automated verification. Errors do not block the transition.

Dev

bun install                # from the @packages/@ts workspace root
bun run typecheck
bun run src/selftest.ts    # headless fixture tests
bun run build              # emit dist/index.js