ultimate-pi 0.3.1 → 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 (114) 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/harness/README.md +2 -1
  58. package/.pi/harness/agents.manifest.json +80 -0
  59. package/.pi/harness/docs/adrs/0009-sentrux-rules-lifecycle.md +9 -5
  60. package/.pi/harness/env.harness.template +28 -0
  61. package/.pi/harness/sentrux/architecture.manifest.json +6 -1
  62. package/.pi/prompts/harness-auto.md +2 -2
  63. package/.pi/prompts/harness-plan.md +2 -2
  64. package/.pi/prompts/harness-router-tune.md +2 -2
  65. package/.pi/prompts/harness-run.md +1 -0
  66. package/.pi/prompts/harness-setup.md +178 -339
  67. package/.pi/scripts/README.md +6 -1
  68. package/.pi/scripts/harness-agents-manifest.mjs +123 -0
  69. package/.pi/scripts/harness-cli-verify.sh +60 -11
  70. package/.pi/scripts/harness-generate-model-router.mjs +242 -0
  71. package/.pi/scripts/harness-graphify-bootstrap.sh +1 -6
  72. package/.pi/scripts/harness-resolve-up-pkg.mjs +71 -0
  73. package/.pi/scripts/harness-seed-project-contracts.mjs +33 -1
  74. package/.pi/scripts/harness-sentrux-bootstrap.mjs +146 -0
  75. package/.pi/scripts/harness-sync-env.mjs +148 -0
  76. package/.pi/scripts/harness-verify.mjs +19 -0
  77. package/.pi/scripts/harness-web-search.md +33 -0
  78. package/.pi/scripts/harness-web.py +177 -0
  79. package/.pi/scripts/harness_web/__init__.py +1 -0
  80. package/.pi/scripts/harness_web/config.py +80 -0
  81. package/.pi/scripts/harness_web/output.py +55 -0
  82. package/.pi/scripts/harness_web/scrape.py +120 -0
  83. package/.pi/scripts/harness_web/search_ddg.py +106 -0
  84. package/.pi/scripts/release.sh +338 -0
  85. package/.pi/scripts/sentrux-rules-sync.mjs +29 -7
  86. package/.pi/settings.example.json +0 -1
  87. package/.sentrux/rules.toml +1 -1
  88. package/AGENTS.md +1 -1
  89. package/CHANGELOG.md +12 -0
  90. package/THIRD_PARTY_NOTICES.md +22 -0
  91. package/package.json +12 -9
  92. package/.agents/skills/firecrawl/SKILL.md +0 -150
  93. package/.agents/skills/firecrawl/rules/install.md +0 -82
  94. package/.agents/skills/firecrawl/rules/security.md +0 -26
  95. package/.agents/skills/firecrawl-agent/SKILL.md +0 -57
  96. package/.agents/skills/firecrawl-build-interact/SKILL.md +0 -67
  97. package/.agents/skills/firecrawl-build-onboarding/SKILL.md +0 -102
  98. package/.agents/skills/firecrawl-build-onboarding/references/auth-flow.md +0 -39
  99. package/.agents/skills/firecrawl-build-onboarding/references/project-setup.md +0 -20
  100. package/.agents/skills/firecrawl-build-onboarding/references/sdk-installation.md +0 -17
  101. package/.agents/skills/firecrawl-build-scrape/SKILL.md +0 -68
  102. package/.agents/skills/firecrawl-build-search/SKILL.md +0 -68
  103. package/.agents/skills/firecrawl-crawl/SKILL.md +0 -58
  104. package/.agents/skills/firecrawl-download/SKILL.md +0 -69
  105. package/.agents/skills/firecrawl-interact/SKILL.md +0 -83
  106. package/.agents/skills/firecrawl-map/SKILL.md +0 -50
  107. package/.agents/skills/firecrawl-parse/SKILL.md +0 -61
  108. package/.agents/skills/firecrawl-scrape/SKILL.md +0 -68
  109. package/.agents/skills/firecrawl-search/SKILL.md +0 -59
  110. package/firecrawl/.env.template +0 -62
  111. package/firecrawl/README.md +0 -49
  112. package/firecrawl/docker-compose.yaml +0 -201
  113. package/firecrawl/searxng/searxng.env +0 -3
  114. 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
 
@@ -74,7 +105,7 @@ bash "$UP_PKG/.pi/scripts/harness-graphify-bootstrap.sh"
74
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:
75
106
 
76
107
  1. Install `graphifyy` (`uv tool install` preferred; else `pip`/`pip3 install --user`)
77
- 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`.
78
109
  3. `GRAPHIFY_VIZ_NODE_LIMIT=200000 graphify update .` — **required**; exits non-zero on failure
79
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)
80
111
  5. `graphify hook install` only when `.git/` exists
@@ -93,133 +124,22 @@ Read and summarize `graphify-out/GRAPH_REPORT.md` — god nodes and surprising c
93
124
  | `graph.json` exists but 0 nodes | Stale/partial output — re-run with `--force` |
94
125
  | `graphify extract` fails | No API key — code graph from `update` is still valid; note in report |
95
126
 
96
- ## Step 1.5 — Optional Self-Hosted Firecrawl
97
-
98
- Ask: "Use self-hosted Firecrawl (local Docker) or cloud (api.firecrawl.dev)? [cloud/self]"
99
- Default: **cloud**.
100
-
101
- If user chooses **self**:
102
-
103
- ### 1.5.1 — Docker Engine Install
104
-
105
- Check if Docker is already available:
106
- ```bash
107
- if ! command -v docker &>/dev/null; then
108
- # Detect OS and install Docker Engine
109
- if [ -f /etc/os-release ]; then
110
- . /etc/os-release
111
- case "$ID" in
112
- ubuntu|debian)
113
- curl -fsSL https://get.docker.com | sh
114
- ;;
115
- fedora|rhel|centos)
116
- curl -fsSL https://get.docker.com | sh
117
- ;;
118
- arch)
119
- pacman -S --noconfirm docker
120
- ;;
121
- *)
122
- echo "Unsupported distro: $ID. Install Docker manually: https://docs.docker.com/engine/install/"
123
- ;;
124
- esac
125
- elif command -v brew &>/dev/null; then
126
- # macOS — install Docker Desktop via brew
127
- brew install --cask docker
128
- else
129
- echo "Cannot detect OS. Install Docker manually: https://docs.docker.com/engine/install/"
130
- fi
131
-
132
- # Enable and start Docker
133
- sudo systemctl enable --now docker 2>/dev/null || true
134
-
135
- # Add current user to docker group (no sudo needed)
136
- sudo usermod -aG docker $USER 2>/dev/null || true
137
- newgrp docker 2>/dev/null || echo "Docker group added. Restart terminal or run: newgrp docker"
138
- fi
139
- ```
140
-
141
- Verify:
142
- ```bash
143
- docker --version
144
- docker compose version
145
- ```
146
-
147
- Block if Docker install fails. Show manual install link.
148
-
149
- ### 1.5.2 — Set Up Self-Hosted Firecrawl Files
150
-
151
- The `firecrawl/` directory in the project root contains all self-hosted config:
152
-
153
- ```
154
- firecrawl/
155
- ├── docker-compose.yaml # Multi-service compose (API, Playwright, Redis, RabbitMQ, Postgres, SearXNG)
156
- ├── README.md # Self-hosted usage docs
157
- ├── .env.template # Environment variables template
158
- └── searxng/
159
- ├── searxng.env # SearXNG-specific env
160
- └── settings.yml # SearXNG engine config
161
- ```
162
-
163
- Create `.env` from template if missing:
164
- ```bash
165
- if [ ! -f firecrawl/.env ]; then
166
- if [ -f firecrawl/.env.template ]; then
167
- cp firecrawl/.env.template firecrawl/.env
168
- echo "Created firecrawl/.env from template."
169
- else
170
- cat > firecrawl/.env << 'EOF'
171
- # Firecrawl Self-Hosted Configuration
172
- PORT=3002
173
- INTERNAL_PORT=3002
174
- REDIS_URL=redis://redis:6379
175
- POSTGRES_USER=postgres
176
- POSTGRES_PASSWORD=postgres
177
- POSTGRES_DB=postgres
178
- USE_DB_AUTHENTICATION=false
179
- NUM_WORKERS_PER_QUEUE=8
180
- CRAWL_CONCURRENT_REQUESTS=10
181
- MAX_CONCURRENT_JOBS=5
182
- BROWSER_POOL_SIZE=5
183
- BULL_AUTH_KEY=changeme
184
- SEARXNG_EXTERNAL_PORT=8080
185
- # Optional AI: uncomment and set
186
- # OPENAI_API_KEY=
187
- # OPENAI_BASE_URL=
188
- # MODEL_NAME=
189
- # OLLAMA_BASE_URL=
190
- EOF
191
- echo "Created firecrawl/.env with defaults."
192
- fi
193
- fi
194
- ```
127
+ ## Step 1.5 — Scrapling / harness-web (web layer)
195
128
 
196
- ### 1.5.3 Start Services
129
+ No Docker stack or API keys. Installed by `harness-cli-verify.sh` in Step 2; optional early install:
197
130
 
198
131
  ```bash
199
- docker compose -f firecrawl/docker-compose.yaml up -d
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
200
139
  ```
201
140
 
202
- Wait for health:
203
- ```bash
204
- echo "Waiting for services to be healthy..."
205
- for i in $(seq 1 30); do
206
- if curl -sf http://localhost:3002/v1/health &>/dev/null; then
207
- echo "✓ Firecrawl API is healthy"
208
- break
209
- fi
210
- sleep 2
211
- done
212
- ```
213
-
214
- ### 1.5.4 — Verify Self-Hosted Instance
215
-
216
- ```bash
217
- 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"
218
- docker compose -f firecrawl/docker-compose.yaml ps
219
- ```
220
-
221
- If user chose **cloud**, skip all 1.5.x steps. Just note:
222
- > "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.
223
143
 
224
144
  ## Step 2 — Install & Verify Global CLI Tools (skip if `--skip-tools`)
225
145
 
@@ -231,7 +151,7 @@ bash "$UP_PKG/.pi/scripts/harness-cli-verify.sh"
231
151
  # Reinstall everything: bash "$UP_PKG/.pi/scripts/harness-cli-verify.sh" --force
232
152
  ```
233
153
 
234
- **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).
235
155
 
236
156
  **Warnings allowed:** gh (if not authenticated), agent-browser (if OS libs need manual `sudo apt-get install`), ck (empty corpus on tiny repos).
237
157
 
@@ -248,46 +168,25 @@ bash "$UP_PKG/.pi/scripts/harness-cli-verify.sh"
248
168
 
249
169
  **Do not continue** past Step 2 if `harness-cli-verify.sh` exits non-zero.
250
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
+
251
173
  ### Manual reference (if script missing in target repo)
252
174
 
253
175
  Use `bash "$UP_PKG/.pi/scripts/harness-cli-verify.sh"` (see Step 0 for `UP_PKG`), or install tools individually:
254
176
 
255
- ### 2.1 — firecrawl-cli (Web Search + Scrape + Crawl + Interact + Download + Parse)
256
-
257
- ```bash
258
- if ! command -v firecrawl &>/dev/null || [ "$FORCE" = "true" ]; then
259
- npm install -g firecrawl-cli@latest
260
- fi
261
- ```
262
-
263
- Verify:
264
- ```bash
265
- firecrawl --status
266
- ```
177
+ ### 2.1 — scrapling + harness-web (Web Search + Scrape)
267
178
 
268
- **If self-hosted mode (Step 1.5 was chosen):** skip cloud auth. Point CLI at local instance:
269
- ```bash
270
- export FIRECRAWL_API_URL=http://localhost:3002
271
- export FIRECRAWL_API_KEY=""
272
- ```
273
- Add to shell profile for persistence:
274
- ```bash
275
- echo 'export FIRECRAWL_API_URL=http://localhost:3002' >> ~/.bashrc 2>/dev/null
276
- echo 'export FIRECRAWL_API_KEY=""' >> ~/.bashrc 2>/dev/null
277
- ```
179
+ Handled by `harness-cli-verify.sh` (`verify_scrapling`). Manual fallback:
278
180
 
279
- **If cloud mode:** authenticate if not already:
280
181
  ```bash
281
- firecrawl login --browser
282
- # OR
283
- firecrawl login --api-key "<key>"
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
284
187
  ```
285
188
 
286
- Quick smoke test (skills ship with `ultimate-pi` via npm — do **not** run `firecrawl setup skills`):
287
- ```bash
288
- mkdir -p .firecrawl
289
- firecrawl scrape "https://firecrawl.dev" -o .firecrawl/install-check.md
290
- ```
189
+ See `.agents/skills/scrapling-web/SKILL.md`.
291
190
 
292
191
  ### 2.2 — ctx7 (Context7 Library Docs + Skills Management)
293
192
 
@@ -412,35 +311,12 @@ if ! command -v sentrux &>/dev/null || [ "$FORCE" = "true" ]; then
412
311
  fi
413
312
  ```
414
313
 
415
- Verify:
416
- ```bash
417
- sentrux --version && echo "✓ sentrux installed" || echo "✗ sentrux install failed"
418
- ```
419
-
420
314
  Install all 52 language plugins:
421
315
  ```bash
422
316
  sentrux plugin add-standard 2>/dev/null || echo "Plugins already installed or failed"
423
317
  ```
424
318
 
425
- Configure MCP server in `.pi/mcp.json` (see Step 4.3).
426
-
427
- Generate architectural rules from the harness manifest (creates/updates `.sentrux/rules.toml`):
428
- ```bash
429
- node "$UP_PKG/.pi/scripts/sentrux-rules-sync.mjs" --force
430
- # Or in pi: /harness-sentrux-sync
431
- ```
432
-
433
- 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.
434
-
435
- Verify rules:
436
- ```bash
437
- sentrux check . && echo "✓ sentrux rules pass" || echo "✗ sentrux check failed"
438
- ```
439
-
440
- Set up structural regression baseline (optional):
441
- ```bash
442
- sentrux gate --save . 2>/dev/null || echo "Baseline will be saved on first gate run"
443
- ```
319
+ Configure MCP server in `.pi/mcp.json` (see Step 4.2). **Rules.toml bootstrap runs in Step 4.3** (idempotent, merge-safe).
444
320
 
445
321
  ## Step 3 — Pi Extension Packages
446
322
 
@@ -466,170 +342,100 @@ Verify each package:
466
342
  |---------|---------|-------|
467
343
  | `@posthog/pi` | Analytics event capture | F0 |
468
344
  | `pi-lean-ctx` | Context runtime (read/bash/find/grep/MCP bridge) | F0 |
469
- | `@tintinweb/pi-subagents` | L4 critic sub-agent spawn/control | P16 |
345
+ | `harness-subagents` (bundled extension) | L4 sub-agent spawn, blackboard, package agents | P16 |
470
346
  | `@sting8k/pi-vcc` | VCC compaction / conversation memory | Shipped |
471
347
  | `pi-model-router` | Vendored (`vendor/`); activates after `.pi/model-router.json` exists | F0 |
472
348
 
473
349
  ## Step 3.5 — Model Router Configuration (Dynamic)
474
350
 
475
- `.pi/model-router.json` is **user-specific** (differs per user's providers).
476
- 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.
477
352
 
478
- The script below:
479
- 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)
480
- 2. Generates a full `model-router.json` with `auto`, `cheap`, and `deep` profiles
481
- 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`):
482
354
 
483
- ```bash
484
- 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)
485
358
 
359
+ ```bash
486
360
  # Verify vendored extension source ships with ultimate-pi
487
361
  ls "$UP_PKG/vendor/pi-model-router/extensions/index.ts" 2>/dev/null \
488
362
  && echo "✓ vendored pi-model-router" \
489
363
  || echo "✗ missing vendor/pi-model-router"
490
364
 
491
- # Generate config from detected providers (only if missing)
492
- if [ -f .pi/model-router.json ]; then
493
- echo "✓ .pi/model-router.json already exists — preserving user config"
494
- else
495
- node << 'GENDONE'
496
- const fs = require('fs');
497
- const path = '.pi/model-router.json';
498
-
499
- // --- Detect providers from env ---
500
- const hasOpenCode = process.env.OPENAI_API_BASE?.includes('opencode.ai');
501
- const hasOpenAI = !!process.env.OPENAI_API_KEY;
502
- const hasAnthropic = !!process.env.ANTHROPIC_API_KEY;
503
- const hasGoogle = !!process.env.GOOGLE_API_KEY;
504
-
505
- // If opencode gateway is detected, prefer opencode-go/ models
506
- // Otherwise use standard provider prefixes
507
- const P = hasOpenCode ? 'opencode-go' : 'openai';
508
-
509
- function model(prefix, name) { return `${prefix}/${name}`; }
510
-
511
- // Best available high-end model per provider
512
- const highModel = hasOpenCode
513
- ? model('opencode-go', 'deepseek-v4-pro')
514
- : hasAnthropic
515
- ? 'anthropic/claude-sonnet-4-20250514'
516
- : hasGoogle
517
- ? 'google/gemini-2.5-flash-001'
518
- : hasOpenAI
519
- ? model('openai', 'gpt-4o')
520
- : null;
521
-
522
- const mediumModel = hasOpenCode
523
- ? model('opencode-go', 'qwen3.6-plus')
524
- : hasAnthropic
525
- ? 'anthropic/claude-sonnet-4-20250514'
526
- : hasGoogle
527
- ? 'google/gemini-flash-latest'
528
- : hasOpenAI
529
- ? model('openai', 'gpt-4o-mini')
530
- : null;
531
-
532
- const lowModel = hasOpenCode
533
- ? model('opencode-go', 'deepseek-v4-flash')
534
- : hasAnthropic
535
- ? 'anthropic/claude-3-5-haiku-20241022'
536
- : hasGoogle
537
- ? 'google/gemini-flash-lite-latest'
538
- : hasOpenAI
539
- ? model('openai', 'gpt-4o-mini')
540
- : null;
541
-
542
- if (!highModel || !mediumModel || !lowModel) {
543
- console.log('✗ No AI provider env detected — skip model-router.json (set OPENAI_API_BASE for opencode, or ANTHROPIC/GOOGLE/OPENAI keys)');
544
- process.exit(0);
545
- }
546
-
547
- const fallbacks = [];
548
- if (hasAnthropic && !highModel.startsWith('anthropic/')) fallbacks.push('anthropic/claude-3-5-sonnet-20241022');
549
- if (hasGoogle && !highModel.startsWith('google/')) fallbacks.push('google/gemini-flash-latest');
550
-
551
- const config = {
552
- defaultProfile: 'auto',
553
- debug: false,
554
- classifierModel: mediumModel,
555
- phaseBias: 0.5,
556
- maxSessionBudget: 1.0,
557
- largeContextThreshold: 100000,
558
- rules: [
559
- {
560
- matches: ['deploy', 'production', 'release'],
561
- tier: 'high',
562
- reason: 'Safety check for production tasks'
563
- },
564
- { matches: 'changelog', tier: 'low' }
565
- ],
566
- profiles: {
567
- auto: {
568
- high: { model: highModel, thinking: 'high', fallbacks },
569
- medium: { model: mediumModel, thinking: 'medium' },
570
- low: { model: lowModel, thinking: 'low' }
571
- },
572
- cheap: {
573
- high: { model: mediumModel, thinking: 'low' },
574
- medium: { model: lowModel, thinking: 'off' },
575
- low: { model: lowModel, thinking: 'off' }
576
- },
577
- deep: {
578
- high: { model: highModel, thinking: 'xhigh', fallbacks },
579
- medium: { model: mediumModel, thinking: 'medium' },
580
- low: { model: lowModel, thinking: 'low' }
581
- }
582
- }
583
- };
584
-
585
- fs.mkdirSync('.pi', { recursive: true });
586
- fs.writeFileSync(path, JSON.stringify(config, null, 2) + '\n');
587
- console.log('✓ Generated .pi/model-router.json from detected providers:');
588
- if (hasOpenCode) console.log(' Provider: opencode gateway');
589
- if (hasOpenAI) console.log(' Detected: OPENAI_API_KEY');
590
- if (hasAnthropic) console.log(' Detected: ANTHROPIC_API_KEY');
591
- if (hasGoogle) console.log(' Detected: GOOGLE_API_KEY');
592
- console.log(` High tier: ${highModel}`);
593
- console.log(` Medium tier: ${mediumModel}`);
594
- console.log(` Low tier: ${lowModel}`);
595
- GENDONE
596
- 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
597
368
 
598
369
  # Merge router defaults after config exists (never adds npm packages — router is vendored)
599
370
  node "$UP_PKG/.pi/scripts/harness-sync-model-router.mjs"
600
371
  ```
601
372
 
602
- 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`.
603
376
 
604
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`**.
605
378
 
606
379
  Manual override: **`/router profile auto`** anytime after reload if they changed defaults.
607
380
 
608
- ## Step 3.6 — Seed `.pi/agents` (pi-subagents)
381
+ ## Step 3.6 — Harness agents (package-resolved)
609
382
 
610
- `@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`:
611
388
 
612
389
  ```bash
613
- UP_PKG="$(node -p "require('path').dirname(require.resolve('ultimate-pi/package.json'))")"
614
- mkdir -p .pi/agents/harness .pi/agents/pi-pi
615
- for dir in harness pi-pi; do
616
- [ -d "$UP_PKG/.pi/agents/$dir" ] || continue
617
- for f in "$UP_PKG/.pi/agents/$dir"/*.md; do
618
- [ -f "$f" ] || continue
619
- base="$(basename "$f")"
620
- [ -f ".pi/agents/$dir/$base" ] || cp "$f" ".pi/agents/$dir/$base"
621
- done
622
- done
623
- echo "✓ .pi/agents (harness + pi-pi) seeded from package"
390
+ node "$UP_PKG/.pi/scripts/harness-agents-manifest.mjs" --check
624
391
  ```
625
392
 
626
393
  ## Step 4 — Configuration Files
627
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
+
628
433
  ### 4.1 — .gitignore Entries
629
434
 
630
435
  Ensure `.gitignore` contains:
631
436
  ```
632
- .firecrawl/
437
+ .env
438
+ .web/
633
439
  .raw/
634
440
  .vault-meta/
635
441
  .pi/harness/critics/
@@ -668,9 +474,43 @@ This gives agents real-time access to structural health metrics:
668
474
  - `check_rules` — architectural constraint enforcement
669
475
  - `health`, `rescan`, `evolution`, `dsm`, `test_gaps`
670
476
 
671
- ### 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
672
512
 
673
- 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.
674
514
 
675
515
  ```markdown
676
516
  # ultimate-pi: Agentic Harness
@@ -686,7 +526,7 @@ Created: $(date +%Y-%m-%d)
686
526
  - .pi/harness/specs/ → Harness contracts and schema docs
687
527
  - .pi/harness/incidents/ → Incident and override records
688
528
  - `.agents/skills/` (npm package) → Harness skills (no copy into `.pi/skills/` needed)
689
- - `.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)
690
530
 
691
531
  ## Graphify-First Workflow
692
532
 
@@ -758,7 +598,7 @@ ls .pi/model-router.json 2>/dev/null && echo "✓ model-router config" || echo "
758
598
  ls -d ./raw 2>/dev/null && echo "✓ ./raw directory exists" || echo "! ./raw directory missing"
759
599
 
760
600
  # gitignore entries
761
- 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"
762
602
  ```
763
603
 
764
604
  ## Step 6 — Graph Knowledge Report Bootstrap
@@ -772,7 +612,7 @@ import json
772
612
  with open('graphify-out/graph.json') as f:
773
613
  g = json.load(f)
774
614
  nodes = g['nodes']
775
- edges = g['edges']
615
+ edges = g.get('edges', g.get('links', []))
776
616
  communities = len(set(n.get('community', 0) for n in nodes))
777
617
  god_nodes = sorted(nodes, key=lambda n: n.get('degree', 0), reverse=True)[:5]
778
618
  print(f'Nodes: {len(nodes)} | Edges: {len(edges)} | Communities: {communities}')
@@ -790,30 +630,31 @@ Output summary table:
790
630
  |-----------|--------|--------|
791
631
  | Knowledge Graph | ✓/✗ | `graphify-out/graph.json` — graph status |
792
632
  | Graphify Hooks | ✓/✗ | git post-commit/post-checkout hooks |
793
- | firecrawl-cli | ✓/✗ | Auth: yes/no |
633
+ | scrapling / harness-web | ✓/✗ | Auth: yes/no |
794
634
  | ctx7 | ✓/✗ | Login: yes/no |
795
635
  | agent-browser | ✓/✗ | Config: .pi/harness/browser.json |
796
636
  | ck-search | ✓/✗ | MCP: registered/CLI-only |
797
637
  | biome | ✓/✗ | Project config: found/default |
798
638
  | ast-grep | ✓/✗ | AST-aware code search (`sg`)
799
639
  | gh CLI | ✓/✗ | Auth: yes/no |
800
- | 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 |
801
642
  | pi extensions | ✓/✗ | 4 packages |
802
643
  | model router | ✓/✗ | Package + config verified, activation via `/router profile auto` |
644
+ | `.env` | ✓/✗/ask | Created / keys appended / user declined |
803
645
 
804
- | .gitignore | ✓/✗ | 7 entries added |
646
+ | .gitignore | ✓/✗ | entries added (incl. `.env`) |
805
647
  | ./raw directory | ✓/✗ | Created for graphify source ingestion |
806
- | Firecrawl mode | self/cloud | Self-hosted on :3002 / Cloud (api.firecrawl.dev) |
807
- | Docker Engine | ✓/✗/N/A | Installed / Not needed (cloud mode) |
648
+ | harness-web (Scrapling) | ✓/✗ | search + scrape smoke |
808
649
 
809
650
  Next steps:
810
651
  1. If tools missing: re-run with `--force` or install individually
811
652
  2. If graph not built: run `bash "$UP_PKG/.pi/scripts/harness-graphify-bootstrap.sh"` (or `graphify update .` from project root)
812
653
  3. If hooks not installed: run `graphify hook install`
813
654
  4. If gh not authenticated: `gh auth login`
814
- 5. If self-hosted Firecrawl unhealthy: `docker compose -f firecrawl/docker-compose.yaml logs`
815
- 6. If sentrux plugins missing: `sentrux plugin add-standard`
816
- 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"`
817
658
 
818
659
  ## Guard Rails
819
660
 
@@ -823,12 +664,11 @@ Next steps:
823
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.
824
665
  - **Python packages (Graphify)**: Before install, detect via PATH, `pip`/`pip3 show graphifyy`, `uv`, or apt. Prefer `uv tool install graphifyy`.
825
666
  - **Node.js >= 18 required**: Some pi packages use modern Node APIs.
826
- - **Docker required for self-hosted**: Step 1.5 needs Docker Engine + Compose. Block if install fails.
827
- - **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).
828
668
  - **Idempotent**: All checks skip if already installed. `--force` overrides.
829
669
  - **No destructive actions**: Creates files only if missing. Never overwrites existing content.
830
670
  - **Partial success**: If some tools fail, report which and continue. User can fix individually.
831
- - **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.
832
672
 
833
673
 
834
674
  ## Error Handling
@@ -845,18 +685,17 @@ Next steps:
845
685
  | Invalid `graphify .` usage | Replace with `graphify update .` — the `.` subcommand does not exist. |
846
686
  | graphify-out empty / 0 nodes | Re-run `bash "$UP_PKG/.pi/scripts/harness-graphify-bootstrap.sh" --force` from project root. |
847
687
  | graphify hook install fails | Hooks need `.git/` directory. Verify inside git repo. Manual: `git config core.hooksPath .pi/git-hooks` |
848
- | 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. |
849
689
  | gh not installed | Show GitHub CLI install link. Skip label creation. |
850
690
  | pi packages install fail | Show error output. Check npm permissions. |
851
691
  | graph already exists | Report node count. Refresh with `graphify update .` unless user passed `--force`. |
852
692
  | biome.json missing | Create minimal config. |
853
693
  | settings.json not writable | Warn. Settings won't persist across sessions. |
854
694
  | No internet | Block for tool installs. Continue for graphify-only steps if `--skip-tools`. |
855
- | Docker not running | Start: `sudo systemctl start docker`. Block if cannot start. |
856
- | Docker install fails | Show manual link: https://docs.docker.com/engine/install/. Block Step 1.5, continue rest. |
857
- | Port 3002 already in use | Warn. User must free port or change `PORT` in `firecrawl/.env`. |
858
- | Self-hosted health check timeout | Show logs: `docker compose -f firecrawl/docker-compose.yaml logs`. Continue — may need more time. |
859
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` |
860
699
 
861
700
  ## Flags
862
701
 
@@ -864,6 +703,6 @@ Next steps:
864
703
  |------|--------|
865
704
  | `--skip-graphify` | Skip Step 0.5 (graph build). Only when a valid `graphify-out/graph.json` already exists. |
866
705
  | `--skip-tools` | Skip Step 2 (CLI tool installs). Use when tools already set up. |
867
- | `--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. |
868
707
  | `--force` | Reinstall all tools even if already present. Overwrite existing files. |
869
708