diff --git a/.gitignore b/.gitignore index 908b95e..b0b1379 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ Thumbs.db # Editor backups * 2.* +reports/ diff --git a/plan/implementation-plan.md b/plan/implementation-plan.md index 5bc84aa..326cb50 100644 --- a/plan/implementation-plan.md +++ b/plan/implementation-plan.md @@ -291,6 +291,36 @@ The registry is a **notary proxy** — it does not host `.cgp` files. Publishers - A1-A10 validation rejects malformed publishes at registry level ✅ - `cpm init my-skill` creates a valid .cgp skeleton ✅ +### Phase 8: Autonomous Package Management + +**Repos:** `core-mcp-bridges`, `cognitiveosd`, `inference`, `product-specs` + +**Goal:** The Wide Model can autonomously discover, install, and remove packages in response to human requests, with every operation validated by the Raw Model. + +| Task | Dependencies | Est. effort | Status | +|------|-------------|-------------|--------| +| ADR-004: Package Manager MCP Bridge decision record | None | Small | ✅ Done | +| `product-specs/specs/base-prompt.md` — daemon-level system prompt | None | Small | Pending | +| `cognitiveos.package.*` domain in mcp-conventions.md | None | Small | Pending | +| `validate_package_request` RPC in raw-model.md spec | None | Small | Pending | +| Validation-hook section in cognitiveosd-api.md | None | Small | Pending | +| `package-mcp` bridge in core-mcp-bridges/ | MCP server library | Medium | Pending | +| MCP validation hook in cognitiveosd mcp_lifecycle.go | cognitiveosd-api spec | Small | Pending | +| `validate_package_request` handler in cograw | raw-model.md spec | Small | Pending | +| `ValidatePackageRequest` method in raw_client.go | None | Small | Pending | +| `package-mcp` in default MCPBridges config | None | Small | Pending | +| Base system prompt injection in daemon startup | None | Small | Pending | +| Tool validation wiring in handlers.go (toolLoop) | None | Small | Pending | + +**Definition of done:** +- Wide Model calls `cognitiveos.package.search("photo viewer")` → daemon validates → package-mcp runs `cpm search` +- Wide Model calls `cognitiveos.package.install("photo-viewer")` → daemon validates via Raw Model → package-mcp runs `cpm install` +- Raw Model denies install if manifest contains `raw_model` field +- Raw Model enforces rate limit (5 ops / 5 min) +- Read-only ops (search/list/info) go through validation with reduced checks +- Base system prompt tells the Wide Model it can autonomously install capabilities +- All 6 package tools are registered and discoverable via `mcp.list_tools` + ## Build Order with Milestones ``` diff --git a/scripts/healthcheck.sh b/scripts/healthcheck.sh index 7df1ce5..2d73d48 100755 --- a/scripts/healthcheck.sh +++ b/scripts/healthcheck.sh @@ -1,4 +1,5 @@ #!/bin/sh +# shellcheck disable=SC3043,SC2044 set -e # ── Config ── @@ -49,11 +50,10 @@ EOF check() { local name="$1" status="$2" detail="$3" - local tag="" case "$status" in - pass) tag="PASS"; pass=$((pass+1)) ;; - fail) tag="FAIL"; fail=$((fail+1)) ;; - skip) tag="SKIP"; skip=$((skip+1)) ;; + pass) pass=$((pass+1)) ;; + fail) fail=$((fail+1)) ;; + skip) skip=$((skip+1)) ;; esac if [ "$MODE_QUIET" -eq 0 ]; then case "$status" in @@ -89,6 +89,8 @@ clone_repo() { has_go() { command -v go >/dev/null 2>&1; } has_shellcheck() { command -v shellcheck >/dev/null 2>&1; } +has_cmake() { command -v cmake >/dev/null 2>&1; } +has_gcc() { command -v gcc >/dev/null 2>&1; } check_go_build() { local dir="$1" label="$2" @@ -96,7 +98,7 @@ check_go_build() { check "go build $label" skip "go not installed" return fi - if CGO_ENABLED=0 go build -o /dev/null ./... 2>&1; then + if (cd "$dir" && CGO_ENABLED=0 go build -o /dev/null ./... 2>&1); then check "go build $label" pass else check "go build $label" fail "build error" @@ -109,13 +111,92 @@ check_go_vet() { check "go vet $label" skip "go not installed" return fi - if go vet ./... 2>&1; then + if (cd "$dir" && CGO_ENABLED=0 go vet ./... 2>&1); then check "go vet $label" pass else check "go vet $label" fail "vet errors" fi } +check_go_build_cgo() { + local dir="$1" label="$2" + if ! has_go; then + check "cgo build $label" skip "go not installed" + return + fi + if ! has_cmake; then + check "cgo build $label" skip "cmake not installed" + return + fi + if ! has_gcc; then + check "cgo build $label" skip "gcc not installed" + return + fi + local llama_dir="$dir/vendor/llama.cpp" + if [ ! -f "$llama_dir/CMakeLists.txt" ]; then + mkdir -p "$dir/vendor" + git clone --depth=1 https://github.com/ggerganov/llama.cpp.git "$llama_dir" >/dev/null 2>&1 || { + check "cgo build $label" skip "llama.cpp clone failed" + return + } + fi + if [ ! -f "$llama_dir/build/libllama.a" ]; then + (cd "$llama_dir" && cmake -B build -DLLAMA_NATIVE=0 \ + -DBUILD_SHARED_LIBS=0 -DLLAMA_BUILD_TESTS=0 \ + -DLLAMA_BUILD_EXAMPLES=0 -DLLAMA_BUILD_SERVER=0 \ + -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="$PWD/build" && \ + cmake --build build --target llama --config Release -j"$(nproc)") >/dev/null 2>&1 || { + check "cgo build $label" fail "llama.cpp cmake build failed" + return + } + fi + local ggml_inc="$llama_dir/ggml/include" + local ggml_ld="" + for lib in $(find "$llama_dir/build" -name "libggml*.a" -type f); do + libname=$(basename "$lib" .a | sed 's/^lib//') + ggml_ld="${ggml_ld} -l${libname}" + done + ggml_ld="${ggml_ld} -lgomp" + if (cd "$dir" && CGO_ENABLED=1 CGO_CFLAGS="-I$ggml_inc" CGO_LDFLAGS="$ggml_ld" go build -tags=cgo -o /dev/null ./cmd/cograw >/dev/null 2>&1); then + check "cgo build $label" pass + else + check "cgo build $label" fail "cgo build error" + fi +} + +check_go_vet_cgo() { + local dir="$1" label="$2" + if ! has_go; then + check "cgo vet $label" skip "go not installed" + return + fi + if ! has_cmake; then + check "cgo vet $label" skip "cmake not installed" + return + fi + if ! has_gcc; then + check "cgo vet $label" skip "gcc not installed" + return + fi + local llama_dir="$dir/vendor/llama.cpp" + if [ ! -f "$llama_dir/CMakeLists.txt" ] || [ ! -f "$llama_dir/build/libllama.a" ]; then + check "cgo vet $label" skip "llama.cpp not built (cgo build step handles this)" + return + fi + local ggml_inc="$llama_dir/ggml/include" + local ggml_ld="" + for lib in $(find "$llama_dir/build" -name "libggml*.a" -type f); do + libname=$(basename "$lib" .a | sed 's/^lib//') + ggml_ld="${ggml_ld} -l${libname}" + done + ggml_ld="${ggml_ld} -lgomp" + if (cd "$dir" && CGO_ENABLED=1 CGO_CFLAGS="-I$ggml_inc" CGO_LDFLAGS="$ggml_ld" go vet -tags=cgo ./... >/dev/null 2>&1); then + check "cgo vet $label" pass + else + check "cgo vet $label" fail "cgo vet errors" + fi +} + check_sh_syntax() { local dir="$1" label="$2" local errors=0 @@ -139,7 +220,7 @@ check_shellcheck() { local errors=0 for script in "$dir"/scripts/*.sh; do [ -f "$script" ] || continue - shellcheck -s sh -S warning "$script" 2>/dev/null || errors=$((errors+1)) + shellcheck -S warning "$script" 2>/dev/null || errors=$((errors+1)) done if [ "$errors" -eq 0 ]; then check "shellcheck $label" pass @@ -156,11 +237,12 @@ check_build_tags() { [ -f "$p" ] || continue head -1 "$p" | grep -q 'cgo' || { errors=$((errors+1)); check "build tag cgo on $f ($label)" fail "missing //go:build cgo"; } done - for f in cograw_llm.go; do - p="$dir/cmd/cograw/$f" - [ -f "$p" ] || continue - head -1 "$p" | grep -q 'cgo' || { errors=$((errors+1)); check "build tag cgo on $f ($label)" fail "missing //go:build cgo"; } - done + f="cograw_llm.go" + p="$dir/cmd/cograw/$f" + if [ -f "$p" ] && ! head -1 "$p" | grep -q 'cgo'; then + errors=$((errors+1)) + check "build tag cgo on $f ($label)" fail "missing //go:build cgo" + fi for f in cograw_stub.go backend_stub.go; do for d in cmd/cograw internal/server; do p="$dir/$d/$f" @@ -255,9 +337,12 @@ hr # ── Tool availability ── section "Tool Availability" -for tool in git go shellcheck; do +for tool in git go shellcheck cmake; do if command -v "$tool" >/dev/null 2>&1; then - ver=$("$tool" version 2>&1 | head -1) + case "$tool" in + cmake) ver=$(cmake --version 2>&1 | head -1) ;; + *) ver=$("$tool" version 2>&1 | head -1) ;; + esac check "$tool" pass "$ver" else check "$tool" skip "not installed" @@ -328,6 +413,8 @@ for repo in $ALL_REPOS; do *" $repo "*) check_build_tags "$dir" "$repo" check_stale_refs "$dir" "$repo" + check_go_build_cgo "$dir" "$repo" + check_go_vet_cgo "$dir" "$repo" ;; esac