ultimate-pi 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/.agents/skills/harness-decisions/SKILL.md +37 -0
  2. package/.agents/skills/harness-governor/SKILL.md +1 -1
  3. package/.agents/skills/harness-orchestration/SKILL.md +54 -0
  4. package/.agents/skills/harness-plan/SKILL.md +4 -3
  5. package/.agents/skills/harness-sentrux-setup/SKILL.md +57 -0
  6. package/.agents/skills/scrapling-web/SKILL.md +93 -0
  7. package/.pi/PACKAGING.md +2 -2
  8. package/.pi/SYSTEM.md +13 -15
  9. package/.pi/agents/harness/adversary.md +3 -0
  10. package/.pi/agents/harness/evaluator.md +3 -0
  11. package/.pi/agents/harness/executor.md +4 -1
  12. package/.pi/agents/harness/meta-optimizer.md +2 -1
  13. package/.pi/agents/harness/planner.md +22 -1
  14. package/.pi/agents/harness/sentrux-bootstrap.md +42 -0
  15. package/.pi/agents/harness/tie-breaker.md +2 -0
  16. package/.pi/extensions/harness-ask-user.ts +74 -0
  17. package/.pi/extensions/harness-subagents.ts +9 -0
  18. package/.pi/extensions/lib/ask-user/dialog.ts +260 -0
  19. package/.pi/extensions/lib/ask-user/fallback.ts +78 -0
  20. package/.pi/extensions/lib/ask-user/render.ts +66 -0
  21. package/.pi/extensions/lib/ask-user/schema.ts +69 -0
  22. package/.pi/extensions/lib/ask-user/types.ts +41 -0
  23. package/.pi/extensions/lib/ask-user/validate-core.mjs +79 -0
  24. package/.pi/extensions/lib/ask-user/validate.ts +92 -0
  25. package/.pi/extensions/lib/harness-subagents/agent-loader.ts +126 -0
  26. package/.pi/extensions/lib/harness-subagents/agent-manifest.ts +119 -0
  27. package/.pi/extensions/lib/harness-subagents/agent-parser.ts +87 -0
  28. package/.pi/extensions/lib/harness-subagents/blackboard-tool.ts +118 -0
  29. package/.pi/extensions/lib/harness-subagents/blackboard.ts +175 -0
  30. package/.pi/extensions/lib/harness-subagents/spawn-policy.ts +27 -0
  31. package/.pi/extensions/lib/harness-subagents/types-blackboard.ts +27 -0
  32. package/.pi/extensions/lib/harness-subagents/vendored/agent-manager.ts +553 -0
  33. package/.pi/extensions/lib/harness-subagents/vendored/agent-runner.ts +637 -0
  34. package/.pi/extensions/lib/harness-subagents/vendored/agent-types.ts +175 -0
  35. package/.pi/extensions/lib/harness-subagents/vendored/context.ts +59 -0
  36. package/.pi/extensions/lib/harness-subagents/vendored/cross-extension-rpc.ts +134 -0
  37. package/.pi/extensions/lib/harness-subagents/vendored/custom-agents.ts +5 -0
  38. package/.pi/extensions/lib/harness-subagents/vendored/default-agents.ts +123 -0
  39. package/.pi/extensions/lib/harness-subagents/vendored/env.ts +43 -0
  40. package/.pi/extensions/lib/harness-subagents/vendored/group-join.ts +144 -0
  41. package/.pi/extensions/lib/harness-subagents/vendored/index.ts +2447 -0
  42. package/.pi/extensions/lib/harness-subagents/vendored/invocation-config.ts +52 -0
  43. package/.pi/extensions/lib/harness-subagents/vendored/memory.ts +182 -0
  44. package/.pi/extensions/lib/harness-subagents/vendored/model-resolver.ts +92 -0
  45. package/.pi/extensions/lib/harness-subagents/vendored/output-file.ts +115 -0
  46. package/.pi/extensions/lib/harness-subagents/vendored/prompts.ts +103 -0
  47. package/.pi/extensions/lib/harness-subagents/vendored/schedule-store.ts +177 -0
  48. package/.pi/extensions/lib/harness-subagents/vendored/schedule.ts +416 -0
  49. package/.pi/extensions/lib/harness-subagents/vendored/settings.ts +210 -0
  50. package/.pi/extensions/lib/harness-subagents/vendored/skill-loader.ts +108 -0
  51. package/.pi/extensions/lib/harness-subagents/vendored/types.ts +187 -0
  52. package/.pi/extensions/lib/harness-subagents/vendored/ui/agent-widget.ts +637 -0
  53. package/.pi/extensions/lib/harness-subagents/vendored/ui/conversation-viewer.ts +324 -0
  54. package/.pi/extensions/lib/harness-subagents/vendored/ui/schedule-menu.ts +110 -0
  55. package/.pi/extensions/lib/harness-subagents/vendored/usage.ts +71 -0
  56. package/.pi/extensions/lib/harness-subagents/vendored/worktree.ts +195 -0
  57. package/.pi/extensions/policy-gate.ts +18 -0
  58. package/.pi/extensions/provider-payload-sanitize.ts +66 -0
  59. package/.pi/harness/README.md +2 -1
  60. package/.pi/harness/agents.manifest.json +80 -0
  61. package/.pi/harness/docs/adrs/0009-sentrux-rules-lifecycle.md +9 -5
  62. package/.pi/harness/env.harness.template +28 -0
  63. package/.pi/harness/sentrux/architecture.manifest.json +6 -1
  64. package/.pi/prompts/harness-auto.md +2 -2
  65. package/.pi/prompts/harness-plan.md +2 -2
  66. package/.pi/prompts/harness-router-tune.md +2 -2
  67. package/.pi/prompts/harness-run.md +1 -0
  68. package/.pi/prompts/harness-setup.md +182 -339
  69. package/.pi/scripts/README.md +6 -1
  70. package/.pi/scripts/harness-agents-manifest.mjs +123 -0
  71. package/.pi/scripts/harness-cli-verify.sh +60 -11
  72. package/.pi/scripts/harness-generate-model-router.mjs +242 -0
  73. package/.pi/scripts/harness-graphify-bootstrap.sh +1 -6
  74. package/.pi/scripts/harness-resolve-up-pkg.mjs +71 -0
  75. package/.pi/scripts/harness-seed-project-contracts.mjs +81 -0
  76. package/.pi/scripts/harness-sentrux-bootstrap.mjs +146 -0
  77. package/.pi/scripts/harness-sync-env.mjs +148 -0
  78. package/.pi/scripts/harness-verify.mjs +19 -0
  79. package/.pi/scripts/harness-web-search.md +33 -0
  80. package/.pi/scripts/harness-web.py +177 -0
  81. package/.pi/scripts/harness_web/__init__.py +1 -0
  82. package/.pi/scripts/harness_web/config.py +80 -0
  83. package/.pi/scripts/harness_web/output.py +55 -0
  84. package/.pi/scripts/harness_web/scrape.py +120 -0
  85. package/.pi/scripts/harness_web/search_ddg.py +106 -0
  86. package/.pi/scripts/release.sh +338 -0
  87. package/.pi/scripts/sentrux-rules-sync.mjs +29 -7
  88. package/.pi/settings.example.json +0 -1
  89. package/.sentrux/rules.toml +1 -1
  90. package/AGENTS.md +1 -1
  91. package/CHANGELOG.md +20 -0
  92. package/THIRD_PARTY_NOTICES.md +22 -0
  93. package/package.json +12 -9
  94. package/.agents/skills/firecrawl/SKILL.md +0 -150
  95. package/.agents/skills/firecrawl/rules/install.md +0 -82
  96. package/.agents/skills/firecrawl/rules/security.md +0 -26
  97. package/.agents/skills/firecrawl-agent/SKILL.md +0 -57
  98. package/.agents/skills/firecrawl-build-interact/SKILL.md +0 -67
  99. package/.agents/skills/firecrawl-build-onboarding/SKILL.md +0 -102
  100. package/.agents/skills/firecrawl-build-onboarding/references/auth-flow.md +0 -39
  101. package/.agents/skills/firecrawl-build-onboarding/references/project-setup.md +0 -20
  102. package/.agents/skills/firecrawl-build-onboarding/references/sdk-installation.md +0 -17
  103. package/.agents/skills/firecrawl-build-scrape/SKILL.md +0 -68
  104. package/.agents/skills/firecrawl-build-search/SKILL.md +0 -68
  105. package/.agents/skills/firecrawl-crawl/SKILL.md +0 -58
  106. package/.agents/skills/firecrawl-download/SKILL.md +0 -69
  107. package/.agents/skills/firecrawl-interact/SKILL.md +0 -83
  108. package/.agents/skills/firecrawl-map/SKILL.md +0 -50
  109. package/.agents/skills/firecrawl-parse/SKILL.md +0 -61
  110. package/.agents/skills/firecrawl-scrape/SKILL.md +0 -68
  111. package/.agents/skills/firecrawl-search/SKILL.md +0 -59
  112. package/firecrawl/.env.template +0 -62
  113. package/firecrawl/README.md +0 -49
  114. package/firecrawl/docker-compose.yaml +0 -201
  115. package/firecrawl/searxng/searxng.env +0 -3
  116. package/firecrawl/searxng/settings.yml +0 -85
@@ -1,24 +1,42 @@
1
1
  ---
2
- description: Full harness bootstrap — Graphify knowledge graph setup, optional self-hosted firecrawl (Docker), CLI tools install, pi extension packages, and verification. Run once per project.
3
- argument-hint: "[--skip-graphify] [--skip-tools] [--skip-firecrawl-self] [--force]"
2
+ description: Full harness bootstrap — Graphify knowledge graph setup, Scrapling/harness-web install, CLI tools, pi extension packages, and verification. Run once per project.
3
+ argument-hint: "[--skip-graphify] [--skip-tools] [--non-interactive] [--force]"
4
4
  ---
5
5
 
6
6
  # harness-setup — Full Harness Bootstrap
7
7
 
8
8
  Bootstraps the complete ultimate-pi agentic harness: Graphify knowledge graph, CLI tools, pi extension packages, configuration files, and verification. Idempotent — safe to re-run, skips what's already installed.
9
9
 
10
+ ## Agent execution notes (read first)
11
+
12
+ **Prefer bundled scripts over re-implementing steps.** Each script is idempotent. Do not duplicate Step 2 subsections (2.1–2.8) when `harness-cli-verify.sh` already passed.
13
+
14
+ | Pitfall | Correct approach |
15
+ |---------|------------------|
16
+ | `UP_PKG="$(pwd)"` in an **external** repo | Wrong — scripts live in the npm package. Resolve via `harness-resolve-up-pkg.mjs` (see Step 0). |
17
+ | Provider detection from `OPENAI_*` / `ANTHROPIC_*` env only | Wrong for pi users — keys live in `~/.pi/agent/auth.json`. Use `harness-generate-model-router.mjs` (Pi `ModelRegistry.getAvailable()`). |
18
+ | Re-running 2.1–2.8 manually after CLI verify | Wasteful — trust `harness-cli-verify.sh` output; only fix reported ✗ lines. |
19
+ | Overwriting `AGENTS.md` after graphify | Graphify appends a section — **merge**, do not replace (Step 4.3). |
20
+ | `sentrux-rules-sync` without project manifest | Use **`harness-sentrux-bootstrap.mjs`** (Step 4.4) — seeds manifest + idempotent rules sync. |
21
+ | Re-running bootstrap with `--force` on unchanged manifest | Wasteful but safe — default bootstrap skips when hash unchanged; `--force` only after manifest edits. |
22
+ | `graph.json` uses `links`, not `edges` | Step 6 stats: `g.get('edges', g.get('links', []))`. |
23
+ | Guessing harness-web / `.env` defaults when `ask_user` is available | **Mandatory `ask_user`** at Step 4.0 unless `--non-interactive`. |
24
+ | `sudo apt-get` without passwordless sudo | Skip — report manual fix; do not block the rest of setup. |
25
+ | `graphify codex install` | **Never run** — it writes `.codex/hooks.json`. Harness targets pi only (`graphify install --platform pi`). |
26
+ | Overwriting `.env` | Use `harness-sync-env.mjs` — never rewrite; append missing keys only. |
27
+
10
28
  ## Parse arguments
11
29
 
12
30
  Read `$ARGUMENTS` and map flags:
13
31
 
14
32
  - `--skip-graphify`
15
33
  - `--skip-tools`
16
- - `--skip-firecrawl-self`
34
+ - `--non-interactive`
17
35
  - `--force`
18
36
 
19
37
  If a flag is unknown, stop and return:
20
38
 
21
- `Usage: /harness-setup [--skip-graphify] [--skip-tools] [--skip-firecrawl-self] [--force]`
39
+ `Usage: /harness-setup [--skip-graphify] [--skip-tools] [--non-interactive] [--force]`
22
40
 
23
41
  ## Step 0 — Pre-flight Environment Check
24
42
 
@@ -32,16 +50,29 @@ Block if node < 18, npm < 9, or git missing. Report versions and continue.
32
50
 
33
51
  Read `.pi/auto-commit.json` for co-author + branch config.
34
52
 
35
- Resolve the installed **ultimate-pi** package root (works in this repo and after `pi install npm:ultimate-pi`):
53
+ Resolve **`UP_PKG`** (ultimate-pi npm package root **not** the target project cwd):
36
54
 
37
55
  ```bash
38
- UP_PKG="$(node -p "require('path').dirname(require.resolve('ultimate-pi/package.json'))")"
56
+ UP_PKG=""
57
+ for _pkg_root in \
58
+ "$(node -p "try{require('path').dirname(require.resolve('ultimate-pi/package.json'))}catch{''}" 2>/dev/null)" \
59
+ "$(npm root -g 2>/dev/null)/ultimate-pi" \
60
+ "$(pwd)"; do
61
+ [ -n "$_pkg_root" ] || continue
62
+ [ -f "$_pkg_root/.pi/scripts/harness-resolve-up-pkg.mjs" ] || continue
63
+ UP_PKG="$(node "$_pkg_root/.pi/scripts/harness-resolve-up-pkg.mjs")"
64
+ break
65
+ done
66
+ if [ -z "$UP_PKG" ] || [ ! -f "$UP_PKG/.pi/scripts/harness-cli-verify.sh" ]; then
67
+ echo "✗ ultimate-pi package not found. Install: pi install npm:ultimate-pi"
68
+ exit 1
69
+ fi
39
70
  echo "ultimate-pi package: $UP_PKG"
40
71
  ```
41
72
 
42
- For extension package names, read **`$UP_PKG/.pi/settings.example.json`** (shipped template). Merge its `packages` array into the **project** `.pi/settings.json` if missing do not copy the repo-dev `.pi/settings.json` from the package (it may contain `".."` and is not published).
73
+ **Developing ultimate-pi from its git clone:** `$(pwd)` is tried last; it wins only when the clone contains `.pi/scripts/harness-resolve-up-pkg.mjs`.
43
74
 
44
- If `require.resolve('ultimate-pi/package.json')` fails (bare clone without resolving the package name), run from **this repository root**: `UP_PKG="$(pwd)"`.
75
+ For extension package names, read **`$UP_PKG/.pi/settings.example.json`**. **Merge** its `packages` array into the **project** `.pi/settings.json` (add missing entries; keep user packages). Do not copy the repo-dev `.pi/settings.json` from the package (it may contain `".."` and is not published).
45
76
 
46
77
  ## Step 0.5 — Graphify (skip if `--skip-graphify`)
47
78
 
@@ -59,6 +90,10 @@ Run from the **project root** (the external repo root, not ultimate-pi unless th
59
90
  ```bash
60
91
  mkdir -p ./raw .pi/harness/specs .pi/harness/runs .pi/harness/incidents .pi/harness/debates
61
92
 
93
+ # Copy JSON schemas + specs README from the package so plan-packet.schema.json exists
94
+ # in the target repo immediately (before graphify or policy-gated planning).
95
+ node "$UP_PKG/.pi/scripts/harness-seed-project-contracts.mjs" "$(pwd)"
96
+
62
97
  # Bundled with ultimate-pi harness; $UP_PKG is set in Step 0
63
98
  bash "$UP_PKG/.pi/scripts/harness-graphify-bootstrap.sh"
64
99
  # Developing ultimate-pi from repo root: UP_PKG="$(pwd)" then same command
@@ -70,7 +105,7 @@ bash "$UP_PKG/.pi/scripts/harness-graphify-bootstrap.sh"
70
105
  If the bootstrap script is missing, run it from the installed ultimate-pi package (`.pi/scripts/` inside the npm package), or execute equivalent steps manually:
71
106
 
72
107
  1. Install `graphifyy` (`uv tool install` preferred; else `pip`/`pip3 install --user`)
73
- 2. `graphify install --platform pi` (and `graphify cursor install` if `.cursor/` exists)
108
+ 2. `graphify install --platform pi` only. **Do not** run `graphify codex install` or `graphify cursor install`.
74
109
  3. `GRAPHIFY_VIZ_NODE_LIMIT=200000 graphify update .` — **required**; exits non-zero on failure
75
110
  4. If `GEMINI_API_KEY`, `GOOGLE_API_KEY`, `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, or `MOONSHOT_API_KEY` is set: `graphify extract .` for full semantic graph (optional enrichment)
76
111
  5. `graphify hook install` only when `.git/` exists
@@ -89,133 +124,22 @@ Read and summarize `graphify-out/GRAPH_REPORT.md` — god nodes and surprising c
89
124
  | `graph.json` exists but 0 nodes | Stale/partial output — re-run with `--force` |
90
125
  | `graphify extract` fails | No API key — code graph from `update` is still valid; note in report |
91
126
 
92
- ## Step 1.5 — Optional Self-Hosted Firecrawl
93
-
94
- Ask: "Use self-hosted Firecrawl (local Docker) or cloud (api.firecrawl.dev)? [cloud/self]"
95
- Default: **cloud**.
96
-
97
- If user chooses **self**:
98
-
99
- ### 1.5.1 — Docker Engine Install
100
-
101
- Check if Docker is already available:
102
- ```bash
103
- if ! command -v docker &>/dev/null; then
104
- # Detect OS and install Docker Engine
105
- if [ -f /etc/os-release ]; then
106
- . /etc/os-release
107
- case "$ID" in
108
- ubuntu|debian)
109
- curl -fsSL https://get.docker.com | sh
110
- ;;
111
- fedora|rhel|centos)
112
- curl -fsSL https://get.docker.com | sh
113
- ;;
114
- arch)
115
- pacman -S --noconfirm docker
116
- ;;
117
- *)
118
- echo "Unsupported distro: $ID. Install Docker manually: https://docs.docker.com/engine/install/"
119
- ;;
120
- esac
121
- elif command -v brew &>/dev/null; then
122
- # macOS — install Docker Desktop via brew
123
- brew install --cask docker
124
- else
125
- echo "Cannot detect OS. Install Docker manually: https://docs.docker.com/engine/install/"
126
- fi
127
-
128
- # Enable and start Docker
129
- sudo systemctl enable --now docker 2>/dev/null || true
130
-
131
- # Add current user to docker group (no sudo needed)
132
- sudo usermod -aG docker $USER 2>/dev/null || true
133
- newgrp docker 2>/dev/null || echo "Docker group added. Restart terminal or run: newgrp docker"
134
- fi
135
- ```
136
-
137
- Verify:
138
- ```bash
139
- docker --version
140
- docker compose version
141
- ```
142
-
143
- Block if Docker install fails. Show manual install link.
144
-
145
- ### 1.5.2 — Set Up Self-Hosted Firecrawl Files
146
-
147
- The `firecrawl/` directory in the project root contains all self-hosted config:
148
-
149
- ```
150
- firecrawl/
151
- ├── docker-compose.yaml # Multi-service compose (API, Playwright, Redis, RabbitMQ, Postgres, SearXNG)
152
- ├── README.md # Self-hosted usage docs
153
- ├── .env.template # Environment variables template
154
- └── searxng/
155
- ├── searxng.env # SearXNG-specific env
156
- └── settings.yml # SearXNG engine config
157
- ```
158
-
159
- Create `.env` from template if missing:
160
- ```bash
161
- if [ ! -f firecrawl/.env ]; then
162
- if [ -f firecrawl/.env.template ]; then
163
- cp firecrawl/.env.template firecrawl/.env
164
- echo "Created firecrawl/.env from template."
165
- else
166
- cat > firecrawl/.env << 'EOF'
167
- # Firecrawl Self-Hosted Configuration
168
- PORT=3002
169
- INTERNAL_PORT=3002
170
- REDIS_URL=redis://redis:6379
171
- POSTGRES_USER=postgres
172
- POSTGRES_PASSWORD=postgres
173
- POSTGRES_DB=postgres
174
- USE_DB_AUTHENTICATION=false
175
- NUM_WORKERS_PER_QUEUE=8
176
- CRAWL_CONCURRENT_REQUESTS=10
177
- MAX_CONCURRENT_JOBS=5
178
- BROWSER_POOL_SIZE=5
179
- BULL_AUTH_KEY=changeme
180
- SEARXNG_EXTERNAL_PORT=8080
181
- # Optional AI: uncomment and set
182
- # OPENAI_API_KEY=
183
- # OPENAI_BASE_URL=
184
- # MODEL_NAME=
185
- # OLLAMA_BASE_URL=
186
- EOF
187
- echo "Created firecrawl/.env with defaults."
188
- fi
189
- fi
190
- ```
191
-
192
- ### 1.5.3 — Start Services
193
-
194
- ```bash
195
- docker compose -f firecrawl/docker-compose.yaml up -d
196
- ```
197
-
198
- Wait for health:
199
- ```bash
200
- echo "Waiting for services to be healthy..."
201
- for i in $(seq 1 30); do
202
- if curl -sf http://localhost:3002/v1/health &>/dev/null; then
203
- echo "✓ Firecrawl API is healthy"
204
- break
205
- fi
206
- sleep 2
207
- done
208
- ```
127
+ ## Step 1.5 — Scrapling / harness-web (web layer)
209
128
 
210
- ### 1.5.4 Verify Self-Hosted Instance
129
+ No Docker stack or API keys. Installed by `harness-cli-verify.sh` in Step 2; optional early install:
211
130
 
212
131
  ```bash
213
- curl -sf http://localhost:3002/v1/health && echo "✓ Self-hosted Firecrawl running on :3002" || echo "✗ Firecrawl not healthy yet — check: docker compose -f firecrawl/docker-compose.yaml logs"
214
- docker compose -f firecrawl/docker-compose.yaml ps
132
+ command -v uv &>/dev/null || curl -LsSf https://astral.sh/uv/install.sh | sh
133
+ export PATH="$HOME/.local/bin:$PATH"
134
+ uv tool install "scrapling[fetchers]"
135
+ scrapling install # Chromium for default stealth scrape; may need sudo for OS libs on Linux
136
+ mkdir -p .web
137
+ python3 "$UP_PKG/.pi/scripts/harness-web.py" search "ultimate-pi harness" -o .web/smoke-search.json --limit 3
138
+ python3 "$UP_PKG/.pi/scripts/harness-web.py" scrape "https://example.com" -o .web/smoke-page.md --fast
215
139
  ```
216
140
 
217
- If user chose **cloud**, skip all 1.5.x steps. Just note:
218
- > "Using cloud Firecrawl. Ensure `FIRECRAWL_API_KEY` is set. Run `firecrawl login` in Step 2.1."
141
+ - **`--skip-tools`:** skip Step 2 (includes Scrapling verify).
142
+ - On Linux/WSL, if stealth scrape fails, install browser libs from `harness-cli-verify.sh` output or use `--fast` for static targets.
219
143
 
220
144
  ## Step 2 — Install & Verify Global CLI Tools (skip if `--skip-tools`)
221
145
 
@@ -227,7 +151,7 @@ bash "$UP_PKG/.pi/scripts/harness-cli-verify.sh"
227
151
  # Reinstall everything: bash "$UP_PKG/.pi/scripts/harness-cli-verify.sh" --force
228
152
  ```
229
153
 
230
- **Required (script must exit 0):** firecrawl-cli, ctx7, biome, ast-grep (`sg`), sentrux (when harness manifest present).
154
+ **Required (script must exit 0):** scrapling + harness-web smoke, ctx7, biome, ast-grep (`sg`), sentrux (when harness manifest present).
231
155
 
232
156
  **Warnings allowed:** gh (if not authenticated), agent-browser (if OS libs need manual `sudo apt-get install`), ck (empty corpus on tiny repos).
233
157
 
@@ -244,46 +168,25 @@ bash "$UP_PKG/.pi/scripts/harness-cli-verify.sh"
244
168
 
245
169
  **Do not continue** past Step 2 if `harness-cli-verify.sh` exits non-zero.
246
170
 
171
+ **After the script exits 0:** do **not** re-run sections 2.1–2.8 individually unless the script reported a specific ✗ failure. Record auth/warning lines from script output in the final report.
172
+
247
173
  ### Manual reference (if script missing in target repo)
248
174
 
249
175
  Use `bash "$UP_PKG/.pi/scripts/harness-cli-verify.sh"` (see Step 0 for `UP_PKG`), or install tools individually:
250
176
 
251
- ### 2.1 — firecrawl-cli (Web Search + Scrape + Crawl + Interact + Download + Parse)
177
+ ### 2.1 — scrapling + harness-web (Web Search + Scrape)
252
178
 
253
- ```bash
254
- if ! command -v firecrawl &>/dev/null || [ "$FORCE" = "true" ]; then
255
- npm install -g firecrawl-cli@latest
256
- fi
257
- ```
179
+ Handled by `harness-cli-verify.sh` (`verify_scrapling`). Manual fallback:
258
180
 
259
- Verify:
260
181
  ```bash
261
- firecrawl --status
182
+ uv tool install "scrapling[fetchers]"
183
+ scrapling install
184
+ mkdir -p .web
185
+ python3 "$UP_PKG/.pi/scripts/harness-web.py" search "query" -o .web/search.json --limit 5
186
+ python3 "$UP_PKG/.pi/scripts/harness-web.py" scrape "https://example.com" -o .web/page.md --fast
262
187
  ```
263
188
 
264
- **If self-hosted mode (Step 1.5 was chosen):** skip cloud auth. Point CLI at local instance:
265
- ```bash
266
- export FIRECRAWL_API_URL=http://localhost:3002
267
- export FIRECRAWL_API_KEY=""
268
- ```
269
- Add to shell profile for persistence:
270
- ```bash
271
- echo 'export FIRECRAWL_API_URL=http://localhost:3002' >> ~/.bashrc 2>/dev/null
272
- echo 'export FIRECRAWL_API_KEY=""' >> ~/.bashrc 2>/dev/null
273
- ```
274
-
275
- **If cloud mode:** authenticate if not already:
276
- ```bash
277
- firecrawl login --browser
278
- # OR
279
- firecrawl login --api-key "<key>"
280
- ```
281
-
282
- Quick smoke test (skills ship with `ultimate-pi` via npm — do **not** run `firecrawl setup skills`):
283
- ```bash
284
- mkdir -p .firecrawl
285
- firecrawl scrape "https://firecrawl.dev" -o .firecrawl/install-check.md
286
- ```
189
+ See `.agents/skills/scrapling-web/SKILL.md`.
287
190
 
288
191
  ### 2.2 — ctx7 (Context7 Library Docs + Skills Management)
289
192
 
@@ -408,35 +311,12 @@ if ! command -v sentrux &>/dev/null || [ "$FORCE" = "true" ]; then
408
311
  fi
409
312
  ```
410
313
 
411
- Verify:
412
- ```bash
413
- sentrux --version && echo "✓ sentrux installed" || echo "✗ sentrux install failed"
414
- ```
415
-
416
314
  Install all 52 language plugins:
417
315
  ```bash
418
316
  sentrux plugin add-standard 2>/dev/null || echo "Plugins already installed or failed"
419
317
  ```
420
318
 
421
- Configure MCP server in `.pi/mcp.json` (see Step 4.3).
422
-
423
- Generate architectural rules from the harness manifest (creates/updates `.sentrux/rules.toml`):
424
- ```bash
425
- node "$UP_PKG/.pi/scripts/sentrux-rules-sync.mjs" --force
426
- # Or in pi: /harness-sentrux-sync
427
- ```
428
-
429
- Edit layers/boundaries in `.pi/harness/sentrux/architecture.manifest.json` when the repo layout changes, then re-run sync. Custom TOML below the `harness:managed` markers is preserved.
430
-
431
- Verify rules:
432
- ```bash
433
- sentrux check . && echo "✓ sentrux rules pass" || echo "✗ sentrux check failed"
434
- ```
435
-
436
- Set up structural regression baseline (optional):
437
- ```bash
438
- sentrux gate --save . 2>/dev/null || echo "Baseline will be saved on first gate run"
439
- ```
319
+ Configure MCP server in `.pi/mcp.json` (see Step 4.2). **Rules.toml bootstrap runs in Step 4.3** (idempotent, merge-safe).
440
320
 
441
321
  ## Step 3 — Pi Extension Packages
442
322
 
@@ -462,170 +342,100 @@ Verify each package:
462
342
  |---------|---------|-------|
463
343
  | `@posthog/pi` | Analytics event capture | F0 |
464
344
  | `pi-lean-ctx` | Context runtime (read/bash/find/grep/MCP bridge) | F0 |
465
- | `@tintinweb/pi-subagents` | L4 critic sub-agent spawn/control | P16 |
345
+ | `harness-subagents` (bundled extension) | L4 sub-agent spawn, blackboard, package agents | P16 |
466
346
  | `@sting8k/pi-vcc` | VCC compaction / conversation memory | Shipped |
467
347
  | `pi-model-router` | Vendored (`vendor/`); activates after `.pi/model-router.json` exists | F0 |
468
348
 
469
349
  ## Step 3.5 — Model Router Configuration (Dynamic)
470
350
 
471
- `.pi/model-router.json` is **user-specific** (differs per user's providers).
472
- It is gitignored. Generate it dynamically from your `.env`.
351
+ `.pi/model-router.json` is **user-specific** (gitignored). Generate from **Pi authenticated providers** (`~/.pi/agent/auth.json`, OAuth, env) — **not** env-var guessing alone.
473
352
 
474
- The script below:
475
- 1. Detects available AI providers from env vars (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GOOGLE_API_KEY`, and `OPENAI_API_BASE` to detect opencode gateway)
476
- 2. Generates a full `model-router.json` with `auto`, `cheap`, and `deep` profiles
477
- 3. Only writes if file doesn't exist yet (safe to re-run, will skip existing)
353
+ Pi API (see `packages/coding-agent` docs / SDK example `02-custom-model.ts`):
478
354
 
479
- ```bash
480
- UP_PKG="$(node -p "require('path').dirname(require.resolve('ultimate-pi/package.json'))")"
355
+ - `AuthStorage.create()` → credentials store
356
+ - `ModelRegistry.create(authStorage)` → registry
357
+ - `await modelRegistry.getAvailable()` → models with working auth (same as interactive pi)
481
358
 
359
+ ```bash
482
360
  # Verify vendored extension source ships with ultimate-pi
483
361
  ls "$UP_PKG/vendor/pi-model-router/extensions/index.ts" 2>/dev/null \
484
362
  && echo "✓ vendored pi-model-router" \
485
363
  || echo "✗ missing vendor/pi-model-router"
486
364
 
487
- # Generate config from detected providers (only if missing)
488
- if [ -f .pi/model-router.json ]; then
489
- echo "✓ .pi/model-router.json already exists — preserving user config"
490
- else
491
- node << 'GENDONE'
492
- const fs = require('fs');
493
- const path = '.pi/model-router.json';
494
-
495
- // --- Detect providers from env ---
496
- const hasOpenCode = process.env.OPENAI_API_BASE?.includes('opencode.ai');
497
- const hasOpenAI = !!process.env.OPENAI_API_KEY;
498
- const hasAnthropic = !!process.env.ANTHROPIC_API_KEY;
499
- const hasGoogle = !!process.env.GOOGLE_API_KEY;
500
-
501
- // If opencode gateway is detected, prefer opencode-go/ models
502
- // Otherwise use standard provider prefixes
503
- const P = hasOpenCode ? 'opencode-go' : 'openai';
504
-
505
- function model(prefix, name) { return `${prefix}/${name}`; }
506
-
507
- // Best available high-end model per provider
508
- const highModel = hasOpenCode
509
- ? model('opencode-go', 'deepseek-v4-pro')
510
- : hasAnthropic
511
- ? 'anthropic/claude-sonnet-4-20250514'
512
- : hasGoogle
513
- ? 'google/gemini-2.5-flash-001'
514
- : hasOpenAI
515
- ? model('openai', 'gpt-4o')
516
- : null;
517
-
518
- const mediumModel = hasOpenCode
519
- ? model('opencode-go', 'qwen3.6-plus')
520
- : hasAnthropic
521
- ? 'anthropic/claude-sonnet-4-20250514'
522
- : hasGoogle
523
- ? 'google/gemini-flash-latest'
524
- : hasOpenAI
525
- ? model('openai', 'gpt-4o-mini')
526
- : null;
527
-
528
- const lowModel = hasOpenCode
529
- ? model('opencode-go', 'deepseek-v4-flash')
530
- : hasAnthropic
531
- ? 'anthropic/claude-3-5-haiku-20241022'
532
- : hasGoogle
533
- ? 'google/gemini-flash-lite-latest'
534
- : hasOpenAI
535
- ? model('openai', 'gpt-4o-mini')
536
- : null;
537
-
538
- if (!highModel || !mediumModel || !lowModel) {
539
- console.log('✗ No AI provider env detected — skip model-router.json (set OPENAI_API_BASE for opencode, or ANTHROPIC/GOOGLE/OPENAI keys)');
540
- process.exit(0);
541
- }
542
-
543
- const fallbacks = [];
544
- if (hasAnthropic && !highModel.startsWith('anthropic/')) fallbacks.push('anthropic/claude-3-5-sonnet-20241022');
545
- if (hasGoogle && !highModel.startsWith('google/')) fallbacks.push('google/gemini-flash-latest');
546
-
547
- const config = {
548
- defaultProfile: 'auto',
549
- debug: false,
550
- classifierModel: mediumModel,
551
- phaseBias: 0.5,
552
- maxSessionBudget: 1.0,
553
- largeContextThreshold: 100000,
554
- rules: [
555
- {
556
- matches: ['deploy', 'production', 'release'],
557
- tier: 'high',
558
- reason: 'Safety check for production tasks'
559
- },
560
- { matches: 'changelog', tier: 'low' }
561
- ],
562
- profiles: {
563
- auto: {
564
- high: { model: highModel, thinking: 'high', fallbacks },
565
- medium: { model: mediumModel, thinking: 'medium' },
566
- low: { model: lowModel, thinking: 'low' }
567
- },
568
- cheap: {
569
- high: { model: mediumModel, thinking: 'low' },
570
- medium: { model: lowModel, thinking: 'off' },
571
- low: { model: lowModel, thinking: 'off' }
572
- },
573
- deep: {
574
- high: { model: highModel, thinking: 'xhigh', fallbacks },
575
- medium: { model: mediumModel, thinking: 'medium' },
576
- low: { model: lowModel, thinking: 'low' }
577
- }
578
- }
579
- };
580
-
581
- fs.mkdirSync('.pi', { recursive: true });
582
- fs.writeFileSync(path, JSON.stringify(config, null, 2) + '\n');
583
- console.log('✓ Generated .pi/model-router.json from detected providers:');
584
- if (hasOpenCode) console.log(' Provider: opencode gateway');
585
- if (hasOpenAI) console.log(' Detected: OPENAI_API_KEY');
586
- if (hasAnthropic) console.log(' Detected: ANTHROPIC_API_KEY');
587
- if (hasGoogle) console.log(' Detected: GOOGLE_API_KEY');
588
- console.log(` High tier: ${highModel}`);
589
- console.log(` Medium tier: ${mediumModel}`);
590
- console.log(` Low tier: ${lowModel}`);
591
- GENDONE
592
- fi
365
+ # Generate from Pi registry (skips if .pi/model-router.json exists; --force to regenerate)
366
+ node "$UP_PKG/.pi/scripts/harness-generate-model-router.mjs"
367
+ # Preview only: node "$UP_PKG/.pi/scripts/harness-generate-model-router.mjs" --dry-run
593
368
 
594
369
  # Merge router defaults after config exists (never adds npm packages — router is vendored)
595
370
  node "$UP_PKG/.pi/scripts/harness-sync-model-router.mjs"
596
371
  ```
597
372
 
598
- Do NOT block. If generation fails, warn in report and continue (defaults script clears `defaultProvider` if it pointed at `router` while no config file exists).
373
+ If generation prints "No authenticated Pi providers": warn in report user should run **`/login`** in pi (or `pi login`) then re-run Step 3.5. Do **not** infer providers from `OPENAI_API_KEY` alone; pi sessions often use `opencode-go` via auth.json without those env vars.
374
+
375
+ Do NOT block setup. If no config is written, `harness-sync-model-router.mjs` clears a premature `defaultProvider: "router"` in `.pi/settings.json`.
599
376
 
600
377
  **Router onboarding** — The vendored extension starts only after `.pi/model-router.json` appears. Running the script above prepares that file plus optional Pi defaults (**`router` / `auto`**) via `harness-sync-model-router.mjs` when `defaultProvider` was unset—then **`/reload`**.
601
378
 
602
379
  Manual override: **`/router profile auto`** anytime after reload if they changed defaults.
603
380
 
604
- ## Step 3.6 — Seed `.pi/agents` (pi-subagents)
381
+ ## Step 3.6 — Harness agents (package-resolved)
605
382
 
606
- `@tintinweb/pi-subagents` reads agent definitions from **this project's** `.pi/agents/`, not from the installed npm tree. Copy packaged agents when missing (preserves user edits):
383
+ `harness-subagents` loads agents from the installed **`ultimate-pi`** package (`$UP_PKG/.pi/agents/**`) with namespaced ids (`harness/planner`, `pi-pi/agent-expert`). **Do not copy** agents into the project unless you want a deliberate override.
384
+
385
+ Optional per-repo overrides: place `.md` files at the **same relative path** (e.g. `.pi/agents/harness/planner.md` overrides the package planner).
386
+
387
+ Verify manifest drift after `pi update ultimate-pi`:
607
388
 
608
389
  ```bash
609
- UP_PKG="$(node -p "require('path').dirname(require.resolve('ultimate-pi/package.json'))")"
610
- mkdir -p .pi/agents/harness .pi/agents/pi-pi
611
- for dir in harness pi-pi; do
612
- [ -d "$UP_PKG/.pi/agents/$dir" ] || continue
613
- for f in "$UP_PKG/.pi/agents/$dir"/*.md; do
614
- [ -f "$f" ] || continue
615
- base="$(basename "$f")"
616
- [ -f ".pi/agents/$dir/$base" ] || cp "$f" ".pi/agents/$dir/$base"
617
- done
618
- done
619
- echo "✓ .pi/agents (harness + pi-pi) seeded from package"
390
+ node "$UP_PKG/.pi/scripts/harness-agents-manifest.mjs" --check
620
391
  ```
621
392
 
622
393
  ## Step 4 — Configuration Files
623
394
 
395
+ ### 4.0 — Project `.env` (non-destructive)
396
+
397
+ Harness extensions read config from project-root `.env` via `dotenv-loader.ts` on session start. **Never overwrite** an existing `.env`.
398
+
399
+ ```bash
400
+ # If .env exists: append only missing harness keys (preserves all current values)
401
+ node "$UP_PKG/.pi/scripts/harness-sync-env.mjs"
402
+ ```
403
+
404
+ If **no** `.env` at project root:
405
+
406
+ - Unless `--non-interactive`, **call `ask_user`**:
407
+
408
+ ```json
409
+ {
410
+ "question": "No .env at project root. Create one from the harness template?",
411
+ "context": "Non-destructive: only creates if missing; never overwrites existing files.",
412
+ "options": [
413
+ { "title": "Create from harness template", "description": "Runs harness-sync-env.mjs --create-missing" },
414
+ { "title": "Skip for now", "description": "Warn in report; user copies template manually later" }
415
+ ],
416
+ "allowFreeform": false
417
+ }
418
+ ```
419
+
420
+ - On **create**: `node "$UP_PKG/.pi/scripts/harness-sync-env.mjs" --create-missing`
421
+ - On **skip** or `--non-interactive`: warn in report (non-interactive skips creation)
422
+ - If `ask_user` cancelled: stop with `needs_clarification`
423
+
424
+ Rules:
425
+
426
+ - **Do not** `cp` over an existing `.env`.
427
+ - **Do not** edit or remove keys the user already set.
428
+ - Re-runs only add keys from `$UP_PKG/.pi/harness/env.harness.template` that are absent (managed block at EOF).
429
+ - Ensure `.env` is gitignored (Step 4.1).
430
+
431
+ Template keys (placeholders — user fills secrets): `HARNESS_TELEMETRY_ENABLED`, `HARNESS_WEB_*`, `PI_VCC_CONFIG_PATH`, plus commented optional PostHog / Graphify vars.
432
+
624
433
  ### 4.1 — .gitignore Entries
625
434
 
626
435
  Ensure `.gitignore` contains:
627
436
  ```
628
- .firecrawl/
437
+ .env
438
+ .web/
629
439
  .raw/
630
440
  .vault-meta/
631
441
  .pi/harness/critics/
@@ -664,9 +474,43 @@ This gives agents real-time access to structural health metrics:
664
474
  - `check_rules` — architectural constraint enforcement
665
475
  - `health`, `rescan`, `evolution`, `dsm`, `test_gaps`
666
476
 
667
- ### 4.3 — Project AGENTS.md
477
+ ### 4.3 — Sentrux rules bootstrap (required)
478
+
479
+ **Skill:** invoke **harness-sentrux-setup** before hand-editing rules or manifest.
480
+
481
+ **Optional agent:** spawn `harness/sentrux-bootstrap` if Sentrux setup needs a dedicated pass.
482
+
483
+ From **project root**, run the bundled bootstrap (seeds manifest when missing, syncs `.sentrux/rules.toml` without clobbering custom TOML):
484
+
485
+ ```bash
486
+ node "$UP_PKG/.pi/scripts/harness-sentrux-bootstrap.mjs"
487
+ # After editing architecture.manifest.json:
488
+ node "$UP_PKG/.pi/scripts/harness-sentrux-bootstrap.mjs" --force
489
+ # In pi: /harness-sentrux-sync (always --force sync)
490
+ ```
491
+
492
+ | Command | When |
493
+ |---------|------|
494
+ | `harness-sentrux-bootstrap.mjs` (no flags) | `/harness-setup`, first install, re-run safe |
495
+ | `harness-sentrux-bootstrap.mjs --force` | Manifest layers/boundaries/constraints changed |
496
+ | `sentrux-rules-sync.mjs --check` | CI / harness-verify drift only |
497
+ | `/harness-sentrux-sync` | Interactive re-sync from pi |
498
+
499
+ `harness-seed-project-contracts.mjs` (Step 0.5) may copy `architecture.manifest.json` early; bootstrap still personalizes `project` on first seed and writes `rules.toml`.
500
+
501
+ Verify rules:
502
+ ```bash
503
+ sentrux check . && echo "✓ sentrux rules pass" || echo "✗ sentrux check failed"
504
+ ```
505
+
506
+ Set up structural regression baseline (optional):
507
+ ```bash
508
+ sentrux gate --save . 2>/dev/null || echo "Baseline will be saved on first gate run"
509
+ ```
510
+
511
+ ### 4.4 — Project AGENTS.md
668
512
 
669
- Create a minimal `AGENTS.md` in the project root for agent onboarding:
513
+ **Do not overwrite** an existing `AGENTS.md` graphify bootstrap may have appended a `## Graphify` section. If missing, create minimal onboarding content; if present, only add harness subsections that are absent.
670
514
 
671
515
  ```markdown
672
516
  # ultimate-pi: Agentic Harness
@@ -682,7 +526,7 @@ Created: $(date +%Y-%m-%d)
682
526
  - .pi/harness/specs/ → Harness contracts and schema docs
683
527
  - .pi/harness/incidents/ → Incident and override records
684
528
  - `.agents/skills/` (npm package) → Harness skills (no copy into `.pi/skills/` needed)
685
- - `.pi/agents/` → Specialized agents (seed from package — see Step 3.6)
529
+ - `.pi/agents/` → Optional per-repo agent overrides (package agents load automatically — see Step 3.6)
686
530
 
687
531
  ## Graphify-First Workflow
688
532
 
@@ -754,7 +598,7 @@ ls .pi/model-router.json 2>/dev/null && echo "✓ model-router config" || echo "
754
598
  ls -d ./raw 2>/dev/null && echo "✓ ./raw directory exists" || echo "! ./raw directory missing"
755
599
 
756
600
  # gitignore entries
757
- grep -q '.firecrawl/' .gitignore 2>/dev/null && echo "✓ .gitignore" || echo "! .gitignore missing entries"
601
+ grep -q '.web/' .gitignore 2>/dev/null && echo "✓ .gitignore" || echo "! .gitignore missing entries"
758
602
  ```
759
603
 
760
604
  ## Step 6 — Graph Knowledge Report Bootstrap
@@ -768,7 +612,7 @@ import json
768
612
  with open('graphify-out/graph.json') as f:
769
613
  g = json.load(f)
770
614
  nodes = g['nodes']
771
- edges = g['edges']
615
+ edges = g.get('edges', g.get('links', []))
772
616
  communities = len(set(n.get('community', 0) for n in nodes))
773
617
  god_nodes = sorted(nodes, key=lambda n: n.get('degree', 0), reverse=True)[:5]
774
618
  print(f'Nodes: {len(nodes)} | Edges: {len(edges)} | Communities: {communities}')
@@ -786,30 +630,31 @@ Output summary table:
786
630
  |-----------|--------|--------|
787
631
  | Knowledge Graph | ✓/✗ | `graphify-out/graph.json` — graph status |
788
632
  | Graphify Hooks | ✓/✗ | git post-commit/post-checkout hooks |
789
- | firecrawl-cli | ✓/✗ | Auth: yes/no |
633
+ | scrapling / harness-web | ✓/✗ | Auth: yes/no |
790
634
  | ctx7 | ✓/✗ | Login: yes/no |
791
635
  | agent-browser | ✓/✗ | Config: .pi/harness/browser.json |
792
636
  | ck-search | ✓/✗ | MCP: registered/CLI-only |
793
637
  | biome | ✓/✗ | Project config: found/default |
794
638
  | ast-grep | ✓/✗ | AST-aware code search (`sg`)
795
639
  | gh CLI | ✓/✗ | Auth: yes/no |
796
- | sentrux | ✓/✗ | Version + plugins: 52 languages |
640
+ | sentrux | ✓/✗ | CLI + plugins; rules via Step 4.3 bootstrap |
641
+ | Sentrux rules.toml | ✓/✗ | `.sentrux/rules.toml` synced from manifest |
797
642
  | pi extensions | ✓/✗ | 4 packages |
798
643
  | model router | ✓/✗ | Package + config verified, activation via `/router profile auto` |
644
+ | `.env` | ✓/✗/ask | Created / keys appended / user declined |
799
645
 
800
- | .gitignore | ✓/✗ | 7 entries added |
646
+ | .gitignore | ✓/✗ | entries added (incl. `.env`) |
801
647
  | ./raw directory | ✓/✗ | Created for graphify source ingestion |
802
- | Firecrawl mode | self/cloud | Self-hosted on :3002 / Cloud (api.firecrawl.dev) |
803
- | Docker Engine | ✓/✗/N/A | Installed / Not needed (cloud mode) |
648
+ | harness-web (Scrapling) | ✓/✗ | search + scrape smoke |
804
649
 
805
650
  Next steps:
806
651
  1. If tools missing: re-run with `--force` or install individually
807
652
  2. If graph not built: run `bash "$UP_PKG/.pi/scripts/harness-graphify-bootstrap.sh"` (or `graphify update .` from project root)
808
653
  3. If hooks not installed: run `graphify hook install`
809
654
  4. If gh not authenticated: `gh auth login`
810
- 5. If self-hosted Firecrawl unhealthy: `docker compose -f firecrawl/docker-compose.yaml logs`
811
- 6. If sentrux plugins missing: `sentrux plugin add-standard`
812
- 7. First harness run: `/harness "your task description"`
655
+ 5. If sentrux plugins missing: `sentrux plugin add-standard`
656
+ 7. If rules.toml missing or out of date: `node "$UP_PKG/.pi/scripts/harness-sentrux-bootstrap.mjs" --force`
657
+ 8. First harness run: `/harness "your task description"`
813
658
 
814
659
  ## Guard Rails
815
660
 
@@ -819,12 +664,11 @@ Next steps:
819
664
  - **Graphify bootstrap is mandatory** (unless `--skip-graphify`): Run `bash "$UP_PKG/.pi/scripts/harness-graphify-bootstrap.sh"`. Never use `graphify . --wiki`. Initial setup must run `graphify update .` and verify `graphify-out/graph.json` has nodes.
820
665
  - **Python packages (Graphify)**: Before install, detect via PATH, `pip`/`pip3 show graphifyy`, `uv`, or apt. Prefer `uv tool install graphifyy`.
821
666
  - **Node.js >= 18 required**: Some pi packages use modern Node APIs.
822
- - **Docker required for self-hosted**: Step 1.5 needs Docker Engine + Compose. Block if install fails.
823
- - **Sufficient RAM for self-hosted**: Firecrawl stack needs ~8GB+ free (API: 8G, Playwright: 4G, others).
667
+ - **Scrapling browsers**: `scrapling install` downloads Chromium (~hundreds of MB). Stealth scrape needs OS libs on Linux (see harness-cli-verify).
824
668
  - **Idempotent**: All checks skip if already installed. `--force` overrides.
825
669
  - **No destructive actions**: Creates files only if missing. Never overwrites existing content.
826
670
  - **Partial success**: If some tools fail, report which and continue. User can fix individually.
827
- - **Rate limits**: ctx7 login is optional. firecrawl auth is required for cloud; none needed for self-hosted.
671
+ - **Rate limits**: ctx7 login is optional. harness-web has no API key; respect SERP/site rate limits.
828
672
 
829
673
 
830
674
  ## Error Handling
@@ -841,18 +685,17 @@ Next steps:
841
685
  | Invalid `graphify .` usage | Replace with `graphify update .` — the `.` subcommand does not exist. |
842
686
  | graphify-out empty / 0 nodes | Re-run `bash "$UP_PKG/.pi/scripts/harness-graphify-bootstrap.sh" --force` from project root. |
843
687
  | graphify hook install fails | Hooks need `.git/` directory. Verify inside git repo. Manual: `git config core.hooksPath .pi/git-hooks` |
844
- | firecrawl auth failed | Show manual login instructions. Continue with other tools. |
688
+ | harness-web / scrapling failed | `uv tool install "scrapling[fetchers]" && scrapling install`; re-run harness-cli-verify. |
845
689
  | gh not installed | Show GitHub CLI install link. Skip label creation. |
846
690
  | pi packages install fail | Show error output. Check npm permissions. |
847
691
  | graph already exists | Report node count. Refresh with `graphify update .` unless user passed `--force`. |
848
692
  | biome.json missing | Create minimal config. |
849
693
  | settings.json not writable | Warn. Settings won't persist across sessions. |
850
694
  | No internet | Block for tool installs. Continue for graphify-only steps if `--skip-tools`. |
851
- | Docker not running | Start: `sudo systemctl start docker`. Block if cannot start. |
852
- | Docker install fails | Show manual link: https://docs.docker.com/engine/install/. Block Step 1.5, continue rest. |
853
- | Port 3002 already in use | Warn. User must free port or change `PORT` in `firecrawl/.env`. |
854
- | Self-hosted health check timeout | Show logs: `docker compose -f firecrawl/docker-compose.yaml logs`. Continue — may need more time. |
855
695
  | sentrux install fails | Show install script output. Fallback: download from https://github.com/sentrux/sentrux/releases/latest |
696
+ | No model-router.json / "No authenticated Pi providers" | Run `/login` in pi, then `node "$UP_PKG/.pi/scripts/harness-generate-model-router.mjs" --force` |
697
+ | UP_PKG not found | `pi install npm:ultimate-pi` or `npm i -g ultimate-pi`; verify with `node "$UP_PKG/.pi/scripts/harness-resolve-up-pkg.mjs"` |
698
+ | No `.env` at project root | `ask_user` create vs skip; on create: `harness-sync-env.mjs --create-missing` |
856
699
 
857
700
  ## Flags
858
701
 
@@ -860,6 +703,6 @@ Next steps:
860
703
  |------|--------|
861
704
  | `--skip-graphify` | Skip Step 0.5 (graph build). Only when a valid `graphify-out/graph.json` already exists. |
862
705
  | `--skip-tools` | Skip Step 2 (CLI tool installs). Use when tools already set up. |
863
- | `--skip-firecrawl-self` | Skip Step 1.5 (self-hosted Firecrawl). Always use cloud. |
706
+ | `--non-interactive` | Skip all `ask_user` prompts; skip `.env` creation with warning. CI/automation only. |
864
707
  | `--force` | Reinstall all tools even if already present. Overwrite existing files. |
865
708