ultimate-pi 0.2.3 → 0.2.5
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.
- package/.pi/extensions/custom-header.ts +26 -2
- package/.pi/extensions/lib/harness-paths.ts +55 -0
- package/.pi/extensions/model-router-bootstrap.ts +174 -0
- package/.pi/extensions/sentrux-rules-sync.ts +28 -3
- package/.pi/harness/browser.json +5 -0
- package/.pi/harness/debates/README.md +9 -0
- package/.pi/harness/docs/adrs/0006-sentrux-dual-layer.md +1 -1
- package/.pi/harness/docs/adrs/0009-sentrux-rules-lifecycle.md +2 -2
- package/.pi/harness/incidents/README.md +6 -0
- package/.pi/harness/release-readiness-report.md +128 -0
- package/.pi/harness/router/proposals/canary-proposal.json +96 -0
- package/.pi/harness/runs/019e272f-3eef-7107-9712-ce281de55707-1778773891854/events.jsonl +2 -0
- package/.pi/harness/runs/019e272f-3eef-7107-9712-ce281de55707-1778773891854/trace.json +17 -0
- package/.pi/harness/runs/019e272f-3eef-7107-9712-ce281de55707-1778773912057/events.jsonl +2 -0
- package/.pi/harness/runs/019e272f-3eef-7107-9712-ce281de55707-1778773912057/trace.json +17 -0
- package/.pi/harness/runs/019e2732-8651-74e5-9f5d-4d06c3105f25-1778774086096/events.jsonl +6 -0
- package/.pi/harness/runs/019e2732-8651-74e5-9f5d-4d06c3105f25-1778774086096/trace.json +42 -0
- package/.pi/harness/runs/019e2732-8651-74e5-9f5d-4d06c3105f25-1778774136101/events.jsonl +1 -0
- package/.pi/harness/runs/019e2758-b332-771b-ad6f-54d0d8478768-1778776600591/events.jsonl +2 -0
- package/.pi/harness/runs/019e2758-b332-771b-ad6f-54d0d8478768-1778776600591/trace.json +17 -0
- package/.pi/harness/runs/README.md +6 -0
- package/.pi/harness/runs/budget-events.jsonl +4 -0
- package/.pi/harness/runs/canary-candidate-router.json +72 -0
- package/.pi/harness/runs/canary-evidence.json +9 -0
- package/.pi/harness/runs/index.jsonl +4 -0
- package/.pi/harness/sentrux/architecture.manifest.json +3 -3
- package/.pi/model-router.example.json +27 -0
- package/.pi/prompts/graphify.md +4 -8
- package/.pi/prompts/harness-setup.md +142 -92
- package/.pi/prompts/release.md +225 -0
- package/.pi/scripts/README.md +17 -0
- package/.pi/scripts/harness-cli-verify.sh +294 -0
- package/.pi/scripts/harness-graphify-bootstrap.sh +151 -0
- package/{scripts → .pi/scripts}/harness-verify.mjs +3 -3
- package/{scripts → .pi/scripts}/sentrux-rules-sync.mjs +2 -2
- package/.pi/settings.json +0 -2
- package/.sentrux/.harness-rules-meta.json +2 -2
- package/.sentrux/rules.toml +3 -3
- package/AGENTS.md +12 -0
- package/CHANGELOG.md +21 -0
- package/README.md +39 -350
- package/firecrawl/.env +53 -0
- package/package.json +16 -4
- package/.ckignore +0 -41
- package/.env.example +0 -21
- package/.gitattributes +0 -1
- package/.github/banner-v2.png +0 -0
- package/.github/workflows/lint.yml +0 -33
- package/.github/workflows/publish-github-packages.yml +0 -35
- package/.github/workflows/publish-npm.yml +0 -32
- package/CONTRIBUTING.md +0 -166
- package/lefthook.yml +0 -9
- package/scripts/__pycache__/merge_graphify_corpora.cpython-314.pyc +0 -0
- package/scripts/index_youtube_urls.py +0 -376
- package/scripts/merge_graphify_corpora.py +0 -398
- package/scripts/regen_graphify_html.py +0 -46
- package/test/harness-verify.test.mjs +0 -33
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Release a new version — bump version, generate changelog, tag, and push to trigger GitHub Actions CI/CD publish.
|
|
3
|
+
argument-hint: "[patch|minor|major] [--dry-run]"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# release — Version Bump + Changelog + Tag + Push
|
|
7
|
+
|
|
8
|
+
Releases a new version by bumping `package.json`, generating a `CHANGELOG.md` entry from commits since the last tag, committing, tagging, and pushing the tag. The push triggers `.github/workflows/publish-github-packages.yml` and `.github/workflows/publish-npm.yml`.
|
|
9
|
+
|
|
10
|
+
## Step 0 — Parse arguments
|
|
11
|
+
|
|
12
|
+
Read `$ARGUMENTS`. First positional arg is the bump type:
|
|
13
|
+
|
|
14
|
+
| Arg | Semver | When |
|
|
15
|
+
|-----|--------|------|
|
|
16
|
+
| `patch` | 0.1.X → 0.1.(X+1) | Bug fixes, small changes, chores |
|
|
17
|
+
| `minor` | 0.X.0 → 0.(X+1).0 | New features, dep additions |
|
|
18
|
+
| `major` | X.0.0 → (X+1).0.0 | Breaking changes |
|
|
19
|
+
|
|
20
|
+
If no bump type given: scan commits since last tag for conventional commit prefixes to infer:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
git log $(git describe --tags --abbrev=0 2>/dev/null || echo "")..HEAD --format="%s" 2>/dev/null
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Inference rules:
|
|
27
|
+
- Any `feat!:` or `BREAKING CHANGE` → **major**
|
|
28
|
+
- Any `feat:` → **minor**
|
|
29
|
+
- Everything else (`fix:`, `chore:`, `docs:`, `refactor:`, etc.) → **patch**
|
|
30
|
+
|
|
31
|
+
If no commits since last tag, warn: "No commits since last tag. Nothing to release." and stop.
|
|
32
|
+
|
|
33
|
+
Present the inferred bump type to user. If they passed one explicitly, use that. If `--dry-run`, show what would happen without making changes.
|
|
34
|
+
|
|
35
|
+
## Step 1 — Read current version
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
node -e "console.log(require('./package.json').version)"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Store as `$CURRENT_VERSION`.
|
|
42
|
+
|
|
43
|
+
Compute `$NEW_VERSION`:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# Pseudo-code — use node for precision
|
|
47
|
+
NEW_VERSION=$(node -e "
|
|
48
|
+
const [maj,min,pat] = '$CURRENT_VERSION'.split('.').map(Number);
|
|
49
|
+
const bump = '$BUMP_TYPE';
|
|
50
|
+
if (bump === 'major') console.log((maj+1)+'.0.0');
|
|
51
|
+
else if (bump === 'minor') console.log(maj+'.'+(min+1)+'.0');
|
|
52
|
+
else console.log(maj+'.'+min+'.'+(pat+1));
|
|
53
|
+
")
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Step 2 — Pre-flight checks
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Must be in a git repo
|
|
60
|
+
git rev-parse --is-inside-work-tree || { echo "Not a git repo. Abort."; exit 1; }
|
|
61
|
+
|
|
62
|
+
# Must have a remote
|
|
63
|
+
git remote -v | grep -q origin || { echo "No origin remote. Abort."; exit 1; }
|
|
64
|
+
|
|
65
|
+
# Must be on a clean working tree
|
|
66
|
+
git diff --quiet && git diff --cached --quiet || { echo "Working tree is dirty. Commit or stash changes first."; exit 1; }
|
|
67
|
+
|
|
68
|
+
# Must be on a branch that can push (not detached HEAD)
|
|
69
|
+
git symbolic-ref -q HEAD || { echo "Detached HEAD. Switch to a branch first."; exit 1; }
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Step 3 — Generate changelog
|
|
73
|
+
|
|
74
|
+
Gather commits since last tag:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
|
|
78
|
+
if [ -z "$LAST_TAG" ]; then
|
|
79
|
+
COMMITS=$(git log --oneline --no-merges HEAD)
|
|
80
|
+
else
|
|
81
|
+
COMMITS=$(git log --oneline --no-merges ${LAST_TAG}..HEAD)
|
|
82
|
+
fi
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Parse conventional commit prefixes to group:
|
|
86
|
+
|
|
87
|
+
| Prefix | Changelog Section |
|
|
88
|
+
|--------|-------------------|
|
|
89
|
+
| `feat!:` | ⚠️ Breaking Changes |
|
|
90
|
+
| `feat:` | ✨ Features |
|
|
91
|
+
| `fix:` | 🐛 Fixes |
|
|
92
|
+
| `perf:` | ⚡ Performance |
|
|
93
|
+
| `refactor:` | ♻️ Refactoring |
|
|
94
|
+
| `docs:` | 📖 Documentation |
|
|
95
|
+
| `style:` | 🎨 Style |
|
|
96
|
+
| `test:` | ✅ Tests |
|
|
97
|
+
| `chore:` | 🔧 Chores |
|
|
98
|
+
| `ci:` | 🔄 CI/CD |
|
|
99
|
+
| `build:` | 📦 Build |
|
|
100
|
+
| everything else | 🔧 Chores |
|
|
101
|
+
|
|
102
|
+
Generate the changelog entry:
|
|
103
|
+
|
|
104
|
+
```markdown
|
|
105
|
+
## [v$NEW_VERSION] — $(date +%Y-%m-%d)
|
|
106
|
+
|
|
107
|
+
### $SECTION_NAME
|
|
108
|
+
|
|
109
|
+
- $commit_message (no prefix, just the description)
|
|
110
|
+
|
|
111
|
+
...
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
If `CHANGELOG.md` exists, prepend the new entry after the `# Changelog` heading. If not, create it:
|
|
115
|
+
|
|
116
|
+
```markdown
|
|
117
|
+
# Changelog
|
|
118
|
+
|
|
119
|
+
All notable changes to this project are documented in this file.
|
|
120
|
+
|
|
121
|
+
## [v$NEW_VERSION] — $(date +%Y-%m-%d)
|
|
122
|
+
|
|
123
|
+
### $SECTION
|
|
124
|
+
|
|
125
|
+
- $entry
|
|
126
|
+
|
|
127
|
+
...
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Step 4 — Bump version in package.json
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
npm pkg set version="$NEW_VERSION"
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Verify:
|
|
137
|
+
```bash
|
|
138
|
+
node -e "const v = require('./package.json').version; console.log(v === '$NEW_VERSION' ? '✓ version bumped to $NEW_VERSION' : '✗ version mismatch')"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Step 5 — Dry run check
|
|
142
|
+
|
|
143
|
+
If `--dry-run` flag: print summary and stop. Do NOT commit, tag, or push.
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
DRY RUN — no changes made.
|
|
147
|
+
Version: $CURRENT_VERSION → $NEW_VERSION
|
|
148
|
+
Bump: $BUMP_TYPE
|
|
149
|
+
Commits since $LAST_TAG: $COMMIT_COUNT
|
|
150
|
+
Files that would change:
|
|
151
|
+
- package.json (version)
|
|
152
|
+
- CHANGELOG.md (new entry)
|
|
153
|
+
Tag that would be created: v$NEW_VERSION
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Step 6 — Commit version bump + changelog
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
git add package.json CHANGELOG.md
|
|
160
|
+
|
|
161
|
+
git commit -m "chore(release): bump to v$NEW_VERSION" -m "- Bump version in package.json
|
|
162
|
+
- Add changelog entry for v$NEW_VERSION
|
|
163
|
+
|
|
164
|
+
Commits included:
|
|
165
|
+
$(echo "$COMMITS" | sed 's/^/- /')" -m "Co-authored-by: pi-mono <261679550+pi-mono@users.noreply.github.com>"
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Use the co-author from `.pi/auto-commit.json` if available, otherwise use the default pi-mono co-author.
|
|
169
|
+
|
|
170
|
+
## Step 7 — Create and push tag
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
git tag -a "v$NEW_VERSION" -m "Release v$NEW_VERSION — $BUMP_TYPE bump
|
|
174
|
+
|
|
175
|
+
$(echo "$COMMITS" | sed 's/^/- /')"
|
|
176
|
+
|
|
177
|
+
git push origin "v$NEW_VERSION"
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Important**: Push only the tag, not the branch. The workflows trigger on `v*` tag push.
|
|
181
|
+
- `publish-github-packages.yml` → publishes `@aryaniyaps/ultimate-pi` to GitHub Packages
|
|
182
|
+
- `publish-npm.yml` → publishes `ultimate-pi` to npm registry
|
|
183
|
+
|
|
184
|
+
Optionally also push the commit if user wants the branch updated:
|
|
185
|
+
```bash
|
|
186
|
+
git push origin $(git branch --show-current)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Ask user: "Push the version-bump commit to the current branch too? [Y/n]"
|
|
190
|
+
|
|
191
|
+
## Step 8 — Report
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
✓ Released v$NEW_VERSION ($BUMP_TYPE)
|
|
195
|
+
Tag: v$NEW_VERSION — pushed to origin
|
|
196
|
+
Workflows triggered:
|
|
197
|
+
- .github/workflows/publish-github-packages.yml
|
|
198
|
+
- .github/workflows/publish-npm.yml
|
|
199
|
+
Commit: $(git rev-parse --short HEAD)
|
|
200
|
+
Changelog: CHANGELOG.md updated
|
|
201
|
+
Monitor: https://github.com/aryaniyaps/ultimate-pi/actions
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Guard Rails
|
|
205
|
+
|
|
206
|
+
- **Clean tree required**: Block if uncommitted changes exist.
|
|
207
|
+
- **No duplicate tags**: Block if `v$NEW_VERSION` tag already exists locally or on remote.
|
|
208
|
+
- **No empty releases**: Block if no commits since last tag.
|
|
209
|
+
- **Valid semver only**: Block if current version doesn't parse as `X.Y.Z`.
|
|
210
|
+
- **Dry run safe**: `--dry-run` prints planned changes without modifying anything.
|
|
211
|
+
- **Manual workflow dispatch**: If workflows support `workflow_dispatch`, user can re-trigger manually from GitHub Actions UI if push fails.
|
|
212
|
+
- **Co-author idempotent**: Falls back to default pi-mono if `.pi/auto-commit.json` is missing.
|
|
213
|
+
|
|
214
|
+
## Error Handling
|
|
215
|
+
|
|
216
|
+
| Error | Action |
|
|
217
|
+
|-------|--------|
|
|
218
|
+
| No commits since last tag | Report, suggest making changes first. Stop. |
|
|
219
|
+
| Dirty working tree | Report dirty files. Suggest `git stash` or commit. Stop. |
|
|
220
|
+
| Tag already exists | Report conflict. User must delete old tag or bump differently. Stop. |
|
|
221
|
+
| No origin remote | Report. Suggest `git remote add origin <url>`. Stop. |
|
|
222
|
+
| Detached HEAD | Report. Suggest `git checkout main`. Stop. |
|
|
223
|
+
| Invalid semver | Report current version string. Stop. |
|
|
224
|
+
| npm pkg set fails | Check Node.js and npm version. Report error. Stop. |
|
|
225
|
+
| git push fails | Check auth. Report error. Suggest manual push. |
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Harness CLI scripts
|
|
2
|
+
|
|
3
|
+
These scripts ship inside the `ultimate-pi` npm package under `.pi/scripts/`.
|
|
4
|
+
|
|
5
|
+
Pi's package manifest (`package.json` → `pi`) only loads **extensions**, **skills**, **prompts**, and **themes** — there is no `scripts` field. Harness scripts are invoked via:
|
|
6
|
+
|
|
7
|
+
- `npm run harness:*` (see root `package.json`)
|
|
8
|
+
- Extensions resolving paths with `resolveHarnessScript()` in `.pi/extensions/lib/harness-paths.ts`
|
|
9
|
+
|
|
10
|
+
| Script | npm script |
|
|
11
|
+
|--------|------------|
|
|
12
|
+
| `harness-graphify-bootstrap.sh` | `harness:graphify-bootstrap` |
|
|
13
|
+
| `harness-cli-verify.sh` | `harness:cli-verify` |
|
|
14
|
+
| `harness-verify.mjs` | `harness:verify` |
|
|
15
|
+
| `sentrux-rules-sync.mjs` | `harness:sentrux-sync` |
|
|
16
|
+
|
|
17
|
+
Repo-root `scripts/` (e.g. `regen_graphify_html.py`) is dev-only and excluded from the npm tarball via `.npmignore`.
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# harness-cli-verify — install and smoke-test harness global CLI tools; fix common Linux deps.
|
|
3
|
+
# Used by /harness-setup Step 2. Exit 0 only if all required tools pass verification.
|
|
4
|
+
|
|
5
|
+
set -u
|
|
6
|
+
set -o pipefail
|
|
7
|
+
|
|
8
|
+
FORCE=false
|
|
9
|
+
for arg in "$@"; do
|
|
10
|
+
case "$arg" in
|
|
11
|
+
--force) FORCE=true ;;
|
|
12
|
+
-h | --help)
|
|
13
|
+
echo "Usage: $0 [--force]"
|
|
14
|
+
echo " Installs missing npm globals, fixes Linux browser libs, runs smoke tests."
|
|
15
|
+
exit 0
|
|
16
|
+
;;
|
|
17
|
+
*)
|
|
18
|
+
echo "Unknown argument: $arg" >&2
|
|
19
|
+
exit 2
|
|
20
|
+
;;
|
|
21
|
+
esac
|
|
22
|
+
done
|
|
23
|
+
|
|
24
|
+
export PATH="${HOME}/.local/bin:${PATH}:$(npm prefix -g 2>/dev/null)/bin"
|
|
25
|
+
|
|
26
|
+
ROOT="$(pwd)"
|
|
27
|
+
FAILURES=0
|
|
28
|
+
WARNINGS=0
|
|
29
|
+
|
|
30
|
+
log() { printf '%s\n' "$*"; }
|
|
31
|
+
pass() { log " ✓ $1"; }
|
|
32
|
+
warn() { log " ! $1"; WARNINGS=$((WARNINGS + 1)); }
|
|
33
|
+
fail() { log " ✗ $1"; FAILURES=$((FAILURES + 1)); }
|
|
34
|
+
|
|
35
|
+
have_cmd() { command -v "$1" &>/dev/null; }
|
|
36
|
+
|
|
37
|
+
npm_global_install() {
|
|
38
|
+
local pkg="$1"
|
|
39
|
+
if [ "$FORCE" = true ] || ! have_cmd "$2"; then
|
|
40
|
+
log " installing $pkg..."
|
|
41
|
+
npm install -g "$pkg" || return 1
|
|
42
|
+
fi
|
|
43
|
+
return 0
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
apt_get_cmd() {
|
|
47
|
+
command -v apt-get 2>/dev/null || command -v apt 2>/dev/null || true
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
linux_apt_install() {
|
|
51
|
+
# Install apt packages when available (WSL/Debian/Ubuntu). Best-effort if sudo works.
|
|
52
|
+
local pkgs=("$@")
|
|
53
|
+
[ ${#pkgs[@]} -eq 0 ] && return 0
|
|
54
|
+
local apt_cmd
|
|
55
|
+
apt_cmd="$(apt_get_cmd)"
|
|
56
|
+
if [ -z "$apt_cmd" ]; then
|
|
57
|
+
return 2
|
|
58
|
+
fi
|
|
59
|
+
local missing=()
|
|
60
|
+
for p in "${pkgs[@]}"; do
|
|
61
|
+
dpkg -s "$p" &>/dev/null 2>&1 || missing+=("$p")
|
|
62
|
+
done
|
|
63
|
+
[ ${#missing[@]} -eq 0 ] && return 0
|
|
64
|
+
log " installing apt packages: ${missing[*]}"
|
|
65
|
+
if sudo -n true 2>/dev/null; then
|
|
66
|
+
sudo DEBIAN_FRONTEND=noninteractive "$apt_cmd" update -qq
|
|
67
|
+
sudo DEBIAN_FRONTEND=noninteractive "$apt_cmd" install -y "${missing[@]}" || {
|
|
68
|
+
warn "apt install failed — run: sudo apt-get install -y ${missing[*]}"
|
|
69
|
+
return 1
|
|
70
|
+
}
|
|
71
|
+
else
|
|
72
|
+
return 2
|
|
73
|
+
fi
|
|
74
|
+
return 0
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
linux_pkg_install() {
|
|
78
|
+
# Debian/Ubuntu, RHEL/Fedora, or Arch — best-effort system deps for headless Chrome.
|
|
79
|
+
local pkgs_deb=(
|
|
80
|
+
libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2
|
|
81
|
+
libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1
|
|
82
|
+
libasound2 libpango-1.0-0 libcairo2 libx11-6 libxcb1 libxext6 fonts-liberation
|
|
83
|
+
)
|
|
84
|
+
if [ -n "$(apt_get_cmd)" ]; then
|
|
85
|
+
linux_apt_install "${pkgs_deb[@]}"
|
|
86
|
+
return $?
|
|
87
|
+
fi
|
|
88
|
+
if command -v dnf &>/dev/null && sudo -n true 2>/dev/null; then
|
|
89
|
+
sudo dnf install -y nss nspr atk at-spi2-atk cups-libs libdrm libxkbcommon \
|
|
90
|
+
libXcomposite libXdamage libXfixes libXrandr mesa-libgbm alsa-lib \
|
|
91
|
+
pango cairo libX11 libxcb libXext liberation-fonts 2>/dev/null && return 0
|
|
92
|
+
return 2
|
|
93
|
+
fi
|
|
94
|
+
if command -v pacman &>/dev/null && sudo -n true 2>/dev/null; then
|
|
95
|
+
sudo pacman -S --noconfirm nss nspr atk at-spi2-atk cups libdrm libxkbcommon \
|
|
96
|
+
libxcomposite libxdamage libxfixes libxrandr mesa gbm alsa-lib \
|
|
97
|
+
pango cairo libx11 libxcb libxext ttf-liberation 2>/dev/null && return 0
|
|
98
|
+
return 2
|
|
99
|
+
fi
|
|
100
|
+
return 2
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# Playwright / agent-browser Chrome on Linux (libnspr4.so, etc.)
|
|
104
|
+
ensure_linux_browser_deps() {
|
|
105
|
+
[ "$(uname -s)" != "Linux" ] && return 0
|
|
106
|
+
local rc=0
|
|
107
|
+
linux_pkg_install || rc=$?
|
|
108
|
+
if [ "$rc" -eq 2 ]; then
|
|
109
|
+
return 2
|
|
110
|
+
fi
|
|
111
|
+
return 0
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
verify_agent_browser() {
|
|
115
|
+
log "[agent-browser]"
|
|
116
|
+
npm_global_install "agent-browser" "agent-browser" || { fail "agent-browser npm install"; return; }
|
|
117
|
+
|
|
118
|
+
local deps_rc=0
|
|
119
|
+
ensure_linux_browser_deps || deps_rc=$?
|
|
120
|
+
if ! agent-browser install 2>/dev/null; then
|
|
121
|
+
warn "agent-browser install (Chrome binary) failed — may still work with system Chrome"
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
local out
|
|
125
|
+
out="$(agent-browser open "about:blank" 2>&1)" || true
|
|
126
|
+
if echo "$out" | grep -qiE 'shared libraries|libnspr4|cannot open shared object'; then
|
|
127
|
+
warn "Chrome missing system libs — installing OS packages"
|
|
128
|
+
if [ "$deps_rc" -eq 2 ] || ! ensure_linux_browser_deps; then
|
|
129
|
+
warn "Could not auto-install OS packages (need sudo). Debian/Ubuntu: sudo apt-get install -y libnss3 libnspr4 libgbm1 libatk1.0-0 libx11-6"
|
|
130
|
+
fi
|
|
131
|
+
if sudo -n true 2>/dev/null; then
|
|
132
|
+
agent-browser install --with-deps 2>/dev/null || true
|
|
133
|
+
else
|
|
134
|
+
warn "Run manually: agent-browser install --with-deps"
|
|
135
|
+
fi
|
|
136
|
+
out="$(agent-browser open "about:blank" 2>&1)" || true
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
if echo "$out" | grep -qiE 'shared libraries|libnspr4|Auto-launch failed'; then
|
|
140
|
+
if [ "$deps_rc" -eq 2 ]; then
|
|
141
|
+
warn "agent-browser needs Linux system libs (manual): sudo apt-get install -y libnss3 libnspr4 libgbm1 && agent-browser install --with-deps"
|
|
142
|
+
else
|
|
143
|
+
fail "agent-browser runtime failed after dep install — see stderr above"
|
|
144
|
+
fi
|
|
145
|
+
else
|
|
146
|
+
pass "agent-browser $(agent-browser --version 2>/dev/null | head -1)"
|
|
147
|
+
agent-browser close 2>/dev/null || true
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
mkdir -p .pi/harness
|
|
151
|
+
if [ ! -f .pi/harness/browser.json ]; then
|
|
152
|
+
echo '{"headless": true, "timeout": 30000, "viewport": {"width": 1280, "height": 720}}' >.pi/harness/browser.json
|
|
153
|
+
fi
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
verify_firecrawl() {
|
|
157
|
+
log "[firecrawl-cli]"
|
|
158
|
+
npm_global_install "firecrawl-cli@latest" "firecrawl" || { fail "firecrawl-cli npm install"; return; }
|
|
159
|
+
if firecrawl --status &>/dev/null; then
|
|
160
|
+
pass "firecrawl $(firecrawl --status 2>/dev/null | head -1 || echo ok)"
|
|
161
|
+
else
|
|
162
|
+
fail "firecrawl --status failed (run: firecrawl login)"
|
|
163
|
+
fi
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
verify_ctx7() {
|
|
167
|
+
log "[ctx7]"
|
|
168
|
+
npm_global_install "ctx7@latest" "ctx7" || { fail "ctx7 npm install"; return; }
|
|
169
|
+
if ctx7 --help &>/dev/null; then
|
|
170
|
+
pass "ctx7"
|
|
171
|
+
else
|
|
172
|
+
fail "ctx7 --help failed"
|
|
173
|
+
fi
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
verify_ck() {
|
|
177
|
+
log "[ck-search]"
|
|
178
|
+
npm_global_install "@beaconbay/ck-search" "ck" || { fail "ck-search npm install"; return; }
|
|
179
|
+
if ! ck --version &>/dev/null; then
|
|
180
|
+
fail "ck --version failed"
|
|
181
|
+
return
|
|
182
|
+
fi
|
|
183
|
+
# Fast grep-mode smoke (no embedding model download)
|
|
184
|
+
local ck_target="."
|
|
185
|
+
[ -d .pi ] && ck_target=".pi"
|
|
186
|
+
if ck -l 1 "export" "$ck_target" 2>/dev/null | head -1 | grep -q .; then
|
|
187
|
+
pass "ck $(ck --version 2>/dev/null | head -1)"
|
|
188
|
+
elif ck --status "$ck_target" 2>/dev/null | head -1 | grep -q .; then
|
|
189
|
+
pass "ck $(ck --version 2>/dev/null | head -1) (index status ok)"
|
|
190
|
+
else
|
|
191
|
+
warn "ck installed but smoke search empty"
|
|
192
|
+
fi
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
verify_biome() {
|
|
196
|
+
log "[biome]"
|
|
197
|
+
npm_global_install "@biomejs/biome" "biome" || { fail "biome npm install"; return; }
|
|
198
|
+
if biome --version &>/dev/null; then
|
|
199
|
+
pass "biome $(biome --version 2>/dev/null | head -1)"
|
|
200
|
+
else
|
|
201
|
+
fail "biome --version failed"
|
|
202
|
+
fi
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
verify_sg() {
|
|
206
|
+
log "[ast-grep]"
|
|
207
|
+
npm_global_install "@ast-grep/cli@latest" "sg" || { fail "ast-grep npm install"; return; }
|
|
208
|
+
if ! sg --version &>/dev/null; then
|
|
209
|
+
fail "sg --version failed"
|
|
210
|
+
return
|
|
211
|
+
fi
|
|
212
|
+
if sg -p 'export' -l ts .pi 2>/dev/null | head -1 | grep -q .; then
|
|
213
|
+
pass "ast-grep $(sg --version 2>/dev/null | head -1)"
|
|
214
|
+
else
|
|
215
|
+
# Still pass if binary works
|
|
216
|
+
pass "ast-grep $(sg --version 2>/dev/null | head -1) (pattern scan skipped)"
|
|
217
|
+
fi
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
verify_gh() {
|
|
221
|
+
log "[gh]"
|
|
222
|
+
if ! have_cmd gh; then
|
|
223
|
+
if [ -n "$(apt_get_cmd)" ] && sudo -n true 2>/dev/null; then
|
|
224
|
+
log " installing gh via apt..."
|
|
225
|
+
sudo DEBIAN_FRONTEND=noninteractive "$(apt_get_cmd)" update -qq
|
|
226
|
+
sudo DEBIAN_FRONTEND=noninteractive "$(apt_get_cmd)" install -y gh 2>/dev/null || true
|
|
227
|
+
fi
|
|
228
|
+
fi
|
|
229
|
+
if have_cmd gh && gh --version &>/dev/null; then
|
|
230
|
+
pass "gh $(gh --version 2>/dev/null | head -1)"
|
|
231
|
+
if gh auth status &>/dev/null 2>&1; then
|
|
232
|
+
pass "gh authenticated"
|
|
233
|
+
if [ -d .git ]; then
|
|
234
|
+
gh label create "harness" --color "0366d6" --description "Agentic harness managed" 2>/dev/null || true
|
|
235
|
+
gh label create "harness-spec" --color "0e8a16" --description "Hardened specification" 2>/dev/null || true
|
|
236
|
+
gh label create "harness-plan" --color "fbca04" --description "Structured plan generated" 2>/dev/null || true
|
|
237
|
+
gh label create "harness-critic" --color "d73a4a" --description "Adversarial review" 2>/dev/null || true
|
|
238
|
+
fi
|
|
239
|
+
else
|
|
240
|
+
warn "gh not authenticated (run: gh auth login)"
|
|
241
|
+
fi
|
|
242
|
+
else
|
|
243
|
+
warn "gh not installed — https://cli.github.com/ (optional for issue specs)"
|
|
244
|
+
fi
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
verify_sentrux() {
|
|
248
|
+
log "[sentrux]"
|
|
249
|
+
if ! have_cmd sentrux || [ "$FORCE" = true ]; then
|
|
250
|
+
if curl -fsSL https://raw.githubusercontent.com/sentrux/sentrux/main/install.sh | sh; then
|
|
251
|
+
export PATH="${HOME}/.local/bin:${PATH}"
|
|
252
|
+
else
|
|
253
|
+
fail "sentrux install script failed"
|
|
254
|
+
return
|
|
255
|
+
fi
|
|
256
|
+
fi
|
|
257
|
+
if ! sentrux --version &>/dev/null; then
|
|
258
|
+
fail "sentrux --version failed"
|
|
259
|
+
return
|
|
260
|
+
fi
|
|
261
|
+
sentrux plugin add-standard 2>/dev/null || warn "sentrux plugin add-standard skipped"
|
|
262
|
+
if [ -f package.json ] && grep -q harness:sentrux-sync package.json 2>/dev/null; then
|
|
263
|
+
npm run harness:sentrux-sync 2>/dev/null || warn "npm run harness:sentrux-sync failed (needs package.json scripts)"
|
|
264
|
+
fi
|
|
265
|
+
if sentrux check . &>/dev/null; then
|
|
266
|
+
pass "sentrux $(sentrux --version 2>/dev/null | head -1)"
|
|
267
|
+
else
|
|
268
|
+
warn "sentrux check . failed (rules may need manifest sync)"
|
|
269
|
+
fi
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
log "Harness CLI verification (cwd: $ROOT)"
|
|
273
|
+
log ""
|
|
274
|
+
|
|
275
|
+
verify_firecrawl
|
|
276
|
+
verify_ctx7
|
|
277
|
+
verify_agent_browser
|
|
278
|
+
verify_ck
|
|
279
|
+
verify_biome
|
|
280
|
+
verify_sg
|
|
281
|
+
verify_gh
|
|
282
|
+
verify_sentrux
|
|
283
|
+
|
|
284
|
+
log ""
|
|
285
|
+
if [ "$FAILURES" -gt 0 ]; then
|
|
286
|
+
log "FAILED: $FAILURES required tool(s). Fix errors above and re-run."
|
|
287
|
+
exit 1
|
|
288
|
+
fi
|
|
289
|
+
if [ "$WARNINGS" -gt 0 ]; then
|
|
290
|
+
log "OK with $WARNINGS warning(s) (optional tools or auth)."
|
|
291
|
+
else
|
|
292
|
+
log "All harness CLI tools verified."
|
|
293
|
+
fi
|
|
294
|
+
exit 0
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# harness-graphify-bootstrap — install graphify and build graphify-out for the current repo.
|
|
3
|
+
# Used by /harness-setup. Do not use deprecated `graphify . --wiki` (invalid CLI).
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
FORCE=false
|
|
8
|
+
for arg in "$@"; do
|
|
9
|
+
case "$arg" in
|
|
10
|
+
--force) FORCE=true ;;
|
|
11
|
+
-h | --help)
|
|
12
|
+
echo "Usage: $0 [--force]"
|
|
13
|
+
echo " --force rebuild even when graphify-out/graph.json already exists"
|
|
14
|
+
exit 0
|
|
15
|
+
;;
|
|
16
|
+
*)
|
|
17
|
+
echo "Unknown argument: $arg" >&2
|
|
18
|
+
exit 2
|
|
19
|
+
;;
|
|
20
|
+
esac
|
|
21
|
+
done
|
|
22
|
+
|
|
23
|
+
export PATH="${HOME}/.local/bin:${PATH}"
|
|
24
|
+
|
|
25
|
+
log() { printf '%s\n' "$*"; }
|
|
26
|
+
die() { printf 'error: %s\n' "$*" >&2; exit 1; }
|
|
27
|
+
|
|
28
|
+
# Python 3.10+
|
|
29
|
+
if ! python3 --version 2>/dev/null | grep -qE 'Python 3\.(1[0-9]|[2-9][0-9])'; then
|
|
30
|
+
die "Python 3.10+ required (got: $(python3 --version 2>/dev/null || echo missing))"
|
|
31
|
+
fi
|
|
32
|
+
log "✓ Python 3.10+"
|
|
33
|
+
|
|
34
|
+
PIP_CMD=""
|
|
35
|
+
command -v pip &>/dev/null && PIP_CMD=pip
|
|
36
|
+
[ -z "$PIP_CMD" ] && command -v pip3 &>/dev/null && PIP_CMD=pip3
|
|
37
|
+
|
|
38
|
+
graphify_installed() {
|
|
39
|
+
command -v graphify &>/dev/null && return 0
|
|
40
|
+
[ -n "$PIP_CMD" ] && $PIP_CMD show graphifyy &>/dev/null 2>&1 && return 0
|
|
41
|
+
command -v uv &>/dev/null && uv pip show graphifyy &>/dev/null 2>&1 && return 0
|
|
42
|
+
command -v uv &>/dev/null && uv tool list 2>/dev/null | grep -qE '(^|[[:space:]])graphifyy([[:space:]]|$)' && return 0
|
|
43
|
+
dpkg -l 2>/dev/null | grep -qE '^ii[[:space:]]+(python3-)?graphify' && return 0
|
|
44
|
+
apt list --installed 2>/dev/null | grep -qiE '(^|/)python3?-?graphify' && return 0
|
|
45
|
+
return 1
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
install_graphify() {
|
|
49
|
+
if command -v uv &>/dev/null; then
|
|
50
|
+
uv tool install graphifyy
|
|
51
|
+
elif [ -n "$PIP_CMD" ]; then
|
|
52
|
+
$PIP_CMD install --user graphifyy
|
|
53
|
+
else
|
|
54
|
+
die "Need uv, pip, or pip3 to install graphifyy"
|
|
55
|
+
fi
|
|
56
|
+
export PATH="${HOME}/.local/bin:${PATH}"
|
|
57
|
+
command -v graphify &>/dev/null || die "graphify not on PATH after install (try: export PATH=\"\$HOME/.local/bin:\$PATH\")"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
graphify_platform_install() {
|
|
61
|
+
graphify install --platform pi 2>/dev/null || graphify pi install 2>/dev/null || true
|
|
62
|
+
if [ -d .cursor ]; then
|
|
63
|
+
graphify cursor install 2>/dev/null || true
|
|
64
|
+
fi
|
|
65
|
+
if [ -f AGENTS.md ] || [ -d .pi ]; then
|
|
66
|
+
graphify codex install 2>/dev/null || true
|
|
67
|
+
fi
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
graph_is_valid() {
|
|
71
|
+
python3 - <<'PY'
|
|
72
|
+
import json
|
|
73
|
+
import sys
|
|
74
|
+
from pathlib import Path
|
|
75
|
+
|
|
76
|
+
root = Path("graphify-out")
|
|
77
|
+
gj = root / "graph.json"
|
|
78
|
+
gr = root / "GRAPH_REPORT.md"
|
|
79
|
+
if not gj.is_file() or not gr.is_file():
|
|
80
|
+
sys.exit(1)
|
|
81
|
+
data = json.loads(gj.read_text(encoding="utf-8"))
|
|
82
|
+
nodes = data.get("nodes") or []
|
|
83
|
+
if len(nodes) < 1:
|
|
84
|
+
sys.exit(1)
|
|
85
|
+
edges = data.get("edges") or data.get("links") or []
|
|
86
|
+
print(f"nodes={len(nodes)} edges={len(edges)}")
|
|
87
|
+
PY
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
has_llm_key() {
|
|
91
|
+
[ -n "${GEMINI_API_KEY:-}" ] || [ -n "${GOOGLE_API_KEY:-}" ] || \
|
|
92
|
+
[ -n "${MOONSHOT_API_KEY:-}" ] || [ -n "${ANTHROPIC_API_KEY:-}" ] || \
|
|
93
|
+
[ -n "${OPENAI_API_KEY:-}" ]
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
mkdir -p graphify-out ./raw
|
|
97
|
+
|
|
98
|
+
if ! graphify_installed; then
|
|
99
|
+
log "Installing graphifyy..."
|
|
100
|
+
install_graphify
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
command -v graphify &>/dev/null || die "graphify CLI not found after install"
|
|
104
|
+
log "✓ graphify ($(command -v graphify))"
|
|
105
|
+
|
|
106
|
+
graphify_platform_install
|
|
107
|
+
|
|
108
|
+
NEED_BUILD=true
|
|
109
|
+
if [ "$FORCE" = false ] && graph_is_valid 2>/dev/null; then
|
|
110
|
+
NEED_BUILD=false
|
|
111
|
+
log "✓ Existing graphify-out/graph.json ($(graph_is_valid))"
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
export GRAPHIFY_VIZ_NODE_LIMIT="${GRAPHIFY_VIZ_NODE_LIMIT:-200000}"
|
|
115
|
+
|
|
116
|
+
if [ "$NEED_BUILD" = true ] || [ "$FORCE" = true ]; then
|
|
117
|
+
log "Building knowledge graph for codebase (graphify update .)..."
|
|
118
|
+
if ! graphify update .; then
|
|
119
|
+
die "graphify update . failed — graphify-out was not created"
|
|
120
|
+
fi
|
|
121
|
+
if ! graph_is_valid 2>/dev/null; then
|
|
122
|
+
die "graphify update . finished but graphify-out/graph.json is missing or empty"
|
|
123
|
+
fi
|
|
124
|
+
log "✓ Code graph built ($(graph_is_valid))"
|
|
125
|
+
if has_llm_key; then
|
|
126
|
+
log "LLM API key detected — running full semantic extract (graphify extract .)..."
|
|
127
|
+
if graphify extract .; then
|
|
128
|
+
if graph_is_valid 2>/dev/null; then
|
|
129
|
+
log "✓ Full graph built ($(graph_is_valid))"
|
|
130
|
+
else
|
|
131
|
+
log "! graphify extract finished but graph validation failed; code-only graph remains"
|
|
132
|
+
fi
|
|
133
|
+
else
|
|
134
|
+
log "! graphify extract failed; keeping code-only graph from graphify update ."
|
|
135
|
+
fi
|
|
136
|
+
else
|
|
137
|
+
log "No LLM API key — code-only graph (AST). Set GEMINI_API_KEY or OPENAI_API_KEY and re-run with --force for semantic extraction."
|
|
138
|
+
fi
|
|
139
|
+
else
|
|
140
|
+
log "Refreshing code graph (graphify update .)..."
|
|
141
|
+
graphify update . || die "graphify update . failed"
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
if [ -d .git ]; then
|
|
145
|
+
graphify hook install 2>/dev/null && log "✓ graphify git hooks installed" || log "! graphify hook install skipped or failed"
|
|
146
|
+
else
|
|
147
|
+
log "! Not a git repo — skipped graphify hook install"
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
log "Graph output: graphify-out/"
|
|
151
|
+
ls -la graphify-out/ 2>/dev/null | head -20 || true
|