steward-cli 0.3.2__tar.gz → 0.5.0__tar.gz
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.
- steward_cli-0.5.0/.claude/skills/discord-notify/SKILL.md +59 -0
- steward_cli-0.5.0/.claude/skills/discord-notify/scripts/send-discord.sh +100 -0
- steward_cli-0.5.0/.claude/skills/gh-issues/SKILL.md +45 -0
- steward_cli-0.5.0/.claude/skills/gh-issues/scripts/gh-issues.sh +52 -0
- steward_cli-0.5.0/.claude/skills/jekyll-test/SKILL.md +57 -0
- steward_cli-0.5.0/.claude/skills/jekyll-test/scripts/test-site.sh +232 -0
- steward_cli-0.5.0/.claude/skills/notebooklm/SKILL.md +47 -0
- steward_cli-0.5.0/.claude/skills/notebooklm/scripts/get-repo-sources.sh +200 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/.claude/skills/pr-review/SKILL.md +8 -4
- steward_cli-0.5.0/.claude/skills/pr-review/scripts/_resolve-nick.sh +43 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/.claude/skills/pr-review/scripts/pr-reply.sh +16 -5
- steward_cli-0.5.0/.claude/skills/pypi-maintainer/SKILL.md +75 -0
- steward_cli-0.5.0/.claude/skills/pypi-maintainer/scripts/switch-source.sh +102 -0
- steward_cli-0.5.0/.claude/skills/run-tests/SKILL.md +50 -0
- steward_cli-0.5.0/.claude/skills/run-tests/scripts/test.sh +52 -0
- steward_cli-0.5.0/.claude/skills/sonarclaude/SKILL.md +84 -0
- steward_cli-0.5.0/.claude/skills/sonarclaude/scripts/sonar.sh +263 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/CHANGELOG.md +30 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/PKG-INFO +1 -1
- steward_cli-0.5.0/culture.yaml +3 -0
- steward_cli-0.5.0/docs/perfect-patient.md +61 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/docs/skill-sources.md +9 -2
- {steward_cli-0.3.2 → steward_cli-0.5.0}/pyproject.toml +1 -1
- {steward_cli-0.3.2 → steward_cli-0.5.0}/steward/cli/_commands/_corpus.py +12 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/tests/test_corpus.py +2 -0
- steward_cli-0.5.0/tests/test_pr_reply_signature.py +52 -0
- steward_cli-0.5.0/tests/test_resolve_nick.py +82 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/uv.lock +1 -1
- steward_cli-0.3.2/docs/perfect-patient.md +0 -48
- {steward_cli-0.3.2 → steward_cli-0.5.0}/.claude/skills/agent-config/SKILL.md +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/.claude/skills/agent-config/scripts/show.sh +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/.claude/skills/doc-test-alignment/SKILL.md +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/.claude/skills/doc-test-alignment/scripts/check.sh +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/.claude/skills/pr-review/scripts/portability-lint.sh +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/.claude/skills/pr-review/scripts/pr-batch.sh +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/.claude/skills/pr-review/scripts/pr-comments.sh +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/.claude/skills/pr-review/scripts/pr-status.sh +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/.claude/skills/pr-review/scripts/workflow.sh +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/.claude/skills/version-bump/SKILL.md +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/.claude/skills/version-bump/scripts/bump.py +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/.claude/skills.local.yaml.example +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/.flake8 +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/.github/workflows/publish.yml +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/.github/workflows/tests.yml +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/.gitignore +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/.markdownlint-cli2.yaml +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/CLAUDE.md +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/LICENSE +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/README.md +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/docs/sibling-pattern.md +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/steward/__init__.py +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/steward/__main__.py +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/steward/cli/__init__.py +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/steward/cli/_commands/__init__.py +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/steward/cli/_commands/doctor.py +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/steward/cli/_commands/show.py +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/steward/cli/_errors.py +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/steward/cli/_output.py +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/tests/__init__.py +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/tests/test_cli.py +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/tests/test_cli_doctor.py +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/tests/test_cli_doctor_siblings.py +0 -0
- {steward_cli-0.3.2 → steward_cli-0.5.0}/tests/test_skills_convention.py +0 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: discord-notify
|
|
3
|
+
description: >
|
|
4
|
+
Send notifications to Discord via webhook. Use when: a long task completes,
|
|
5
|
+
an error needs attention, the user says "notify me", "ping me", "let me know",
|
|
6
|
+
"send to discord", or "update me when done".
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Discord Notify
|
|
10
|
+
|
|
11
|
+
Send a message to a Discord channel using a webhook embed.
|
|
12
|
+
|
|
13
|
+
## When to Use
|
|
14
|
+
|
|
15
|
+
- A long-running task finishes (build, deploy, migration)
|
|
16
|
+
- An error occurs that the user should know about
|
|
17
|
+
- The user explicitly asks to be notified
|
|
18
|
+
|
|
19
|
+
## Prerequisites
|
|
20
|
+
|
|
21
|
+
The script requires the following tools on `PATH`:
|
|
22
|
+
|
|
23
|
+
- `bash`
|
|
24
|
+
- `curl` — sends the webhook POST
|
|
25
|
+
- `jq` — builds the JSON payload safely
|
|
26
|
+
|
|
27
|
+
## Environment
|
|
28
|
+
|
|
29
|
+
Set `DISCORD_WEBHOOK_URL` to a Discord webhook URL before use.
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# Basic info message
|
|
35
|
+
bash .claude/skills/discord-notify/scripts/send-discord.sh "Build passed"
|
|
36
|
+
|
|
37
|
+
# Completion (green)
|
|
38
|
+
bash .claude/skills/discord-notify/scripts/send-discord.sh --type completion "Deploy finished"
|
|
39
|
+
|
|
40
|
+
# Error (red)
|
|
41
|
+
bash .claude/skills/discord-notify/scripts/send-discord.sh --type error "Tests failed on main"
|
|
42
|
+
|
|
43
|
+
# Status update (yellow)
|
|
44
|
+
bash .claude/skills/discord-notify/scripts/send-discord.sh --type status "Migration running — 3/10 tables done"
|
|
45
|
+
|
|
46
|
+
# Custom title
|
|
47
|
+
bash .claude/skills/discord-notify/scripts/send-discord.sh --type completion --title "CI Pipeline" "All checks green"
|
|
48
|
+
|
|
49
|
+
# Different sender identity
|
|
50
|
+
bash .claude/skills/discord-notify/scripts/send-discord.sh --username "Codex" "Task complete"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Options
|
|
54
|
+
|
|
55
|
+
| Flag | Short | Default | Description |
|
|
56
|
+
|------|-------|---------|-------------|
|
|
57
|
+
| `--type` | `-t` | `info` | `info` (blue), `status` (yellow), `completion` (green), `error` (red) |
|
|
58
|
+
| `--title` | `-T` | auto | Override the embed title |
|
|
59
|
+
| `--username` | `-u` | `Claude Code` | Sender name shown in Discord |
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Discord webhook notification sender using embeds.
|
|
5
|
+
# Requires DISCORD_WEBHOOK_URL environment variable.
|
|
6
|
+
|
|
7
|
+
# --- Defaults ---
|
|
8
|
+
TYPE="info"
|
|
9
|
+
TITLE=""
|
|
10
|
+
USERNAME="Claude Code"
|
|
11
|
+
MESSAGE=""
|
|
12
|
+
|
|
13
|
+
# --- Color map ---
|
|
14
|
+
color_for_type() {
|
|
15
|
+
case "$1" in
|
|
16
|
+
info) echo 3447003 ;; # blue
|
|
17
|
+
status) echo 16776960 ;; # yellow
|
|
18
|
+
completion) echo 3066993 ;; # green
|
|
19
|
+
error) echo 15158332 ;; # red
|
|
20
|
+
*) echo "Unknown type: $1" >&2; exit 1 ;;
|
|
21
|
+
esac
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
# --- Default title ---
|
|
25
|
+
title_for_type() {
|
|
26
|
+
case "$1" in
|
|
27
|
+
info) echo "Info" ;;
|
|
28
|
+
status) echo "Status" ;;
|
|
29
|
+
completion) echo "Completed" ;;
|
|
30
|
+
error) echo "Error" ;;
|
|
31
|
+
esac
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# --- Parse args ---
|
|
35
|
+
require_value() {
|
|
36
|
+
local flag="$1" remaining="$2"
|
|
37
|
+
if [[ "$remaining" -lt 2 ]]; then
|
|
38
|
+
echo "Error: $flag requires a value" >&2
|
|
39
|
+
echo "Usage: send-discord.sh [--type info|status|completion|error] [--title TITLE] [--username NAME] MESSAGE" >&2
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
while [[ $# -gt 0 ]]; do
|
|
45
|
+
case "$1" in
|
|
46
|
+
--type|-t) require_value "$1" "$#"; TYPE="$2"; shift 2 ;;
|
|
47
|
+
--title|-T) require_value "$1" "$#"; TITLE="$2"; shift 2 ;;
|
|
48
|
+
--username|-u) require_value "$1" "$#"; USERNAME="$2"; shift 2 ;;
|
|
49
|
+
-*) echo "Unknown option: $1" >&2; exit 1 ;;
|
|
50
|
+
*) MESSAGE="$1"; shift ;;
|
|
51
|
+
esac
|
|
52
|
+
done
|
|
53
|
+
|
|
54
|
+
# --- Validate ---
|
|
55
|
+
if [[ -z "${DISCORD_WEBHOOK_URL:-}" ]]; then
|
|
56
|
+
echo "Error: DISCORD_WEBHOOK_URL environment variable is not set." >&2
|
|
57
|
+
exit 1
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
if [[ -z "$MESSAGE" ]]; then
|
|
61
|
+
echo "Error: No message provided." >&2
|
|
62
|
+
echo "Usage: send-discord.sh [--type info|status|completion|error] [--title TITLE] [--username NAME] MESSAGE" >&2
|
|
63
|
+
exit 1
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
COLOR=$(color_for_type "$TYPE")
|
|
67
|
+
EMBED_TITLE="${TITLE:-$(title_for_type "$TYPE")}"
|
|
68
|
+
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
69
|
+
|
|
70
|
+
# --- Build JSON safely with jq ---
|
|
71
|
+
PAYLOAD=$(jq -n \
|
|
72
|
+
--arg username "$USERNAME" \
|
|
73
|
+
--arg title "$EMBED_TITLE" \
|
|
74
|
+
--arg description "$MESSAGE" \
|
|
75
|
+
--argjson color "$COLOR" \
|
|
76
|
+
--arg footer "Sent by $USERNAME" \
|
|
77
|
+
--arg timestamp "$TIMESTAMP" \
|
|
78
|
+
'{
|
|
79
|
+
username: $username,
|
|
80
|
+
embeds: [{
|
|
81
|
+
title: $title,
|
|
82
|
+
description: $description,
|
|
83
|
+
color: $color,
|
|
84
|
+
footer: { text: $footer },
|
|
85
|
+
timestamp: $timestamp
|
|
86
|
+
}]
|
|
87
|
+
}')
|
|
88
|
+
|
|
89
|
+
# --- Send ---
|
|
90
|
+
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
|
|
91
|
+
-H "Content-Type: application/json" \
|
|
92
|
+
-d "$PAYLOAD" \
|
|
93
|
+
"$DISCORD_WEBHOOK_URL")
|
|
94
|
+
|
|
95
|
+
if [[ "$HTTP_CODE" -ge 200 && "$HTTP_CODE" -lt 300 ]]; then
|
|
96
|
+
echo "Sent ($HTTP_CODE)"
|
|
97
|
+
else
|
|
98
|
+
echo "Error: Discord returned HTTP $HTTP_CODE" >&2
|
|
99
|
+
exit 1
|
|
100
|
+
fi
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gh-issues
|
|
3
|
+
description: >
|
|
4
|
+
Fetch GitHub issues with full body and comments. Use when checking issues,
|
|
5
|
+
reviewing bug reports, or the user says "check issues", "fetch issues",
|
|
6
|
+
"issue #N", or references GitHub issue numbers.
|
|
7
|
+
triggers:
|
|
8
|
+
- check issues
|
|
9
|
+
- fetch issues
|
|
10
|
+
- issue #
|
|
11
|
+
- github issues
|
|
12
|
+
- verify issues
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# GitHub Issues Skill
|
|
16
|
+
|
|
17
|
+
Fetch one or more GitHub issues with full body text and all comments.
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
### Single issue
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
bash .claude/skills/gh-issues/scripts/gh-issues.sh 191
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Range
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
bash .claude/skills/gh-issues/scripts/gh-issues.sh 191-197
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Specific list
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
bash .claude/skills/gh-issues/scripts/gh-issues.sh 191 195 197
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Explicit repo
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
bash .claude/skills/gh-issues/scripts/gh-issues.sh --repo owner/repo 42-50
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Output is JSON per issue with: number, title, state, labels, body, and comments array.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Fetch GitHub issues with full body and comments
|
|
3
|
+
# Usage: gh-issues.sh [RANGE|NUMBER] [--repo OWNER/REPO]
|
|
4
|
+
# gh-issues.sh 191-197 # range
|
|
5
|
+
# gh-issues.sh 191 # single
|
|
6
|
+
# gh-issues.sh 191 192 195 # list
|
|
7
|
+
# gh-issues.sh --repo foo/bar 5 # explicit repo
|
|
8
|
+
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
|
|
11
|
+
REPO_FLAG=""
|
|
12
|
+
NUMBERS=()
|
|
13
|
+
|
|
14
|
+
while [[ $# -gt 0 ]]; do
|
|
15
|
+
case "$1" in
|
|
16
|
+
--repo)
|
|
17
|
+
if [[ $# -lt 2 || -z "$2" ]]; then
|
|
18
|
+
echo "Error: --repo requires a value (OWNER/REPO)" >&2
|
|
19
|
+
echo "Usage: gh-issues.sh [RANGE|NUMBER...] [--repo OWNER/REPO]" >&2
|
|
20
|
+
exit 1
|
|
21
|
+
fi
|
|
22
|
+
REPO_FLAG="--repo $2"
|
|
23
|
+
shift 2 ;;
|
|
24
|
+
*-*) # range like 191-197
|
|
25
|
+
IFS='-' read -r start end <<< "$1"
|
|
26
|
+
for ((i=start; i<=end; i++)); do NUMBERS+=("$i"); done
|
|
27
|
+
shift ;;
|
|
28
|
+
*) NUMBERS+=("$1"); shift ;;
|
|
29
|
+
esac
|
|
30
|
+
done
|
|
31
|
+
|
|
32
|
+
if [[ ${#NUMBERS[@]} -eq 0 ]]; then
|
|
33
|
+
echo "Usage: gh-issues.sh [RANGE|NUMBER...] [--repo OWNER/REPO]" >&2
|
|
34
|
+
exit 1
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
for num in "${NUMBERS[@]}"; do
|
|
38
|
+
echo "========================================"
|
|
39
|
+
echo "ISSUE #${num}"
|
|
40
|
+
echo "========================================"
|
|
41
|
+
# shellcheck disable=SC2086
|
|
42
|
+
gh issue view "$num" $REPO_FLAG --json number,title,state,labels,body,comments \
|
|
43
|
+
--jq '{
|
|
44
|
+
number: .number,
|
|
45
|
+
title: .title,
|
|
46
|
+
state: .state,
|
|
47
|
+
labels: [.labels[].name],
|
|
48
|
+
body: .body,
|
|
49
|
+
comments: [.comments[] | {author: .author.login, body: .body}]
|
|
50
|
+
}' 2>/dev/null || echo "ERROR: Could not fetch issue #${num}"
|
|
51
|
+
echo
|
|
52
|
+
done
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: jekyll-test
|
|
3
|
+
description: >
|
|
4
|
+
Build a Jekyll site (GitHub Pages / Cloudflare Pages) and validate the
|
|
5
|
+
output: build succeeds, custom color scheme is applied, permalinked pages
|
|
6
|
+
exist, just-the-docs navigation is consistent. Use after editing
|
|
7
|
+
`_config.yml`, theme files, SCSS, or page front matter; or when the user
|
|
8
|
+
says "test the site", "did the site build", "check jekyll", or
|
|
9
|
+
"verify pages".
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Jekyll Test
|
|
13
|
+
|
|
14
|
+
Conditional skill — only meaningful when the sibling repo ships a Jekyll
|
|
15
|
+
site. Detected by the presence of `_config.yml` somewhere in the repo.
|
|
16
|
+
|
|
17
|
+
The original version of this skill was a ten-step manual checklist. The
|
|
18
|
+
script does all ten steps in one shot and prints a single-screen summary;
|
|
19
|
+
follow the script, not a manual procedure.
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Test the site whose _config.yml is at or above the current directory
|
|
25
|
+
bash .claude/skills/jekyll-test/scripts/test-site.sh
|
|
26
|
+
|
|
27
|
+
# Test a specific site (path to repo root)
|
|
28
|
+
bash .claude/skills/jekyll-test/scripts/test-site.sh /path/to/jekyll-site
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## What the script checks
|
|
32
|
+
|
|
33
|
+
The script walks up to find `_config.yml`, runs `bundle install --quiet`
|
|
34
|
+
and `bundle exec jekyll build` (the only hard-fail step), then reports
|
|
35
|
+
non-fatal validation findings:
|
|
36
|
+
|
|
37
|
+
- Build status and elapsed time.
|
|
38
|
+
- For `color_scheme: <name>` declarations, every hex from
|
|
39
|
+
`_sass/color_schemes/<name>.scss` is grep'd for in the built
|
|
40
|
+
`_site/assets/css/*` — missing colors are listed.
|
|
41
|
+
- Every `permalink:` in front matter is checked against the generated
|
|
42
|
+
`<permalink>/index.html` — missing pages are listed.
|
|
43
|
+
- For `just-the-docs` sites, every `parent:` value is matched against
|
|
44
|
+
parent pages' `title:` — orphans (children whose parent is not found)
|
|
45
|
+
are listed. Parent counts and child counts are printed.
|
|
46
|
+
- `_includes/footer_custom.html` and `_includes/head_custom.html`, if
|
|
47
|
+
they exist, are checked for inclusion in `_site/index.html`.
|
|
48
|
+
|
|
49
|
+
The output is a single summary block plus per-category failure detail.
|
|
50
|
+
|
|
51
|
+
## Requirements
|
|
52
|
+
|
|
53
|
+
- `bundle` (Bundler) on PATH.
|
|
54
|
+
- `ruby` on PATH (whatever the site's `Gemfile` requires).
|
|
55
|
+
- `python3` on PATH (used to parse `_config.yml`; PyYAML preferred,
|
|
56
|
+
regex fallback if it is not installed).
|
|
57
|
+
- A Jekyll project (must contain `_config.yml`).
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test-site.sh — Build a Jekyll site and validate the output.
|
|
3
|
+
# Replaces the manual ten-step checklist that lived in the original
|
|
4
|
+
# jekyll-test SKILL.md.
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# test-site.sh # tests the site at/above $PWD
|
|
8
|
+
# test-site.sh /path/to/site
|
|
9
|
+
#
|
|
10
|
+
# Exits non-zero only if the Jekyll project cannot be found or the build
|
|
11
|
+
# itself fails. Validation findings (missing colors, missing permalinks,
|
|
12
|
+
# orphan navigation children, missing includes) are reported but do not
|
|
13
|
+
# fail the run — the agent reading the output decides what to do.
|
|
14
|
+
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
START_DIR="${1:-$PWD}"
|
|
18
|
+
|
|
19
|
+
# --- Locate Jekyll root (walk up from START_DIR looking for _config.yml) ---
|
|
20
|
+
SITE_ROOT=""
|
|
21
|
+
dir="$(cd "$START_DIR" && pwd)"
|
|
22
|
+
while [[ "$dir" != "/" && "$dir" != "" ]]; do
|
|
23
|
+
if [[ -f "$dir/_config.yml" ]]; then
|
|
24
|
+
SITE_ROOT="$dir"
|
|
25
|
+
break
|
|
26
|
+
fi
|
|
27
|
+
dir="$(dirname "$dir")"
|
|
28
|
+
done
|
|
29
|
+
|
|
30
|
+
if [[ -z "$SITE_ROOT" ]]; then
|
|
31
|
+
echo "Error: no _config.yml found at or above $START_DIR — not a Jekyll project" >&2
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
cd "$SITE_ROOT"
|
|
36
|
+
echo "Site root: $SITE_ROOT"
|
|
37
|
+
|
|
38
|
+
# --- Read selected _config.yml keys via Python (PyYAML if available, else regex) ---
|
|
39
|
+
if ! command -v python3 >/dev/null 2>&1; then
|
|
40
|
+
echo "Error: python3 is required to parse _config.yml but was not found on PATH" >&2
|
|
41
|
+
exit 1
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
read_config() {
|
|
45
|
+
python3 - <<'PY' 2>/dev/null
|
|
46
|
+
import os, sys, re
|
|
47
|
+
path = "_config.yml"
|
|
48
|
+
try:
|
|
49
|
+
import yaml
|
|
50
|
+
with open(path) as f:
|
|
51
|
+
cfg = yaml.safe_load(f) or {}
|
|
52
|
+
except Exception:
|
|
53
|
+
cfg = {}
|
|
54
|
+
line_re = re.compile(r"^(\w+):\s*(.*)$")
|
|
55
|
+
with open(path) as f:
|
|
56
|
+
for line in f:
|
|
57
|
+
m = line_re.match(line)
|
|
58
|
+
if m:
|
|
59
|
+
cfg[m.group(1)] = m.group(2).strip().strip('"').strip("'")
|
|
60
|
+
for key in ("color_scheme", "theme", "remote_theme", "baseurl", "url"):
|
|
61
|
+
val = cfg.get(key, "")
|
|
62
|
+
print(f"{key}={val}")
|
|
63
|
+
PY
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
declare -A CFG=()
|
|
67
|
+
while IFS='=' read -r k v; do
|
|
68
|
+
[[ -n "$k" ]] && CFG["$k"]="$v"
|
|
69
|
+
done < <(read_config)
|
|
70
|
+
|
|
71
|
+
THEME="${CFG[theme]:-${CFG[remote_theme]:-}}"
|
|
72
|
+
COLOR_SCHEME="${CFG[color_scheme]:-}"
|
|
73
|
+
|
|
74
|
+
echo "Theme: ${THEME:-<none>}"
|
|
75
|
+
echo "Color scheme: ${COLOR_SCHEME:-<none>}"
|
|
76
|
+
|
|
77
|
+
# --- Bundler dependencies ---
|
|
78
|
+
if ! command -v bundle >/dev/null 2>&1; then
|
|
79
|
+
echo "Error: bundle (Bundler) is not on PATH — install Ruby + bundler first" >&2
|
|
80
|
+
exit 1
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
if ! bundle check >/dev/null 2>&1; then
|
|
84
|
+
echo "Installing gem dependencies via 'bundle install'..."
|
|
85
|
+
bundle install --quiet
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
# --- just-the-docs search index init (only when used as a gem theme) ---
|
|
89
|
+
if [[ "$THEME" == "just-the-docs" && ! -f "assets/js/zzzz-search-data.json" ]]; then
|
|
90
|
+
echo "Initialising just-the-docs search index..."
|
|
91
|
+
bundle exec just-the-docs rake search:init >/dev/null 2>&1 || true
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
# --- Build ---
|
|
95
|
+
echo
|
|
96
|
+
echo "Building site..."
|
|
97
|
+
BUILD_LOG="$(mktemp)"
|
|
98
|
+
trap 'rm -f "$BUILD_LOG"' EXIT
|
|
99
|
+
BUILD_OK=1
|
|
100
|
+
BUILD_TIME=""
|
|
101
|
+
if bundle exec jekyll build >"$BUILD_LOG" 2>&1; then
|
|
102
|
+
BUILD_TIME="$(grep -oE 'done in [0-9.]+ seconds' "$BUILD_LOG" | tail -1 || echo)"
|
|
103
|
+
else
|
|
104
|
+
BUILD_OK=0
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
if [[ "$BUILD_OK" -eq 0 ]]; then
|
|
108
|
+
echo "Build: FAILED"
|
|
109
|
+
echo "--- last 40 lines of build log ---"
|
|
110
|
+
tail -40 "$BUILD_LOG"
|
|
111
|
+
exit 1
|
|
112
|
+
fi
|
|
113
|
+
echo "Build: OK ${BUILD_TIME:+($BUILD_TIME)}"
|
|
114
|
+
|
|
115
|
+
# --- Color scheme verification ---
|
|
116
|
+
COLOR_FOUND=0
|
|
117
|
+
COLOR_TOTAL=0
|
|
118
|
+
COLOR_MISSING=()
|
|
119
|
+
if [[ -n "$COLOR_SCHEME" ]]; then
|
|
120
|
+
SCSS_FILE="_sass/color_schemes/${COLOR_SCHEME}.scss"
|
|
121
|
+
if [[ -f "$SCSS_FILE" ]]; then
|
|
122
|
+
# Pull every `#abcdef` hex from the SCSS file
|
|
123
|
+
mapfile -t HEXES < <(grep -ohE '#[0-9a-fA-F]{3,8}' "$SCSS_FILE" | sort -u)
|
|
124
|
+
COLOR_TOTAL=${#HEXES[@]}
|
|
125
|
+
if [[ "$COLOR_TOTAL" -gt 0 ]]; then
|
|
126
|
+
CSS_BLOB="$(cat _site/assets/css/*.css 2>/dev/null || true)"
|
|
127
|
+
for hex in "${HEXES[@]}"; do
|
|
128
|
+
if grep -qiF "$hex" <<<"$CSS_BLOB"; then
|
|
129
|
+
((COLOR_FOUND++)) || true
|
|
130
|
+
else
|
|
131
|
+
COLOR_MISSING+=("$hex")
|
|
132
|
+
fi
|
|
133
|
+
done
|
|
134
|
+
fi
|
|
135
|
+
fi
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
# --- Permalink verification ---
|
|
139
|
+
PERMA_TOTAL=0
|
|
140
|
+
PERMA_OK=0
|
|
141
|
+
PERMA_MISSING=()
|
|
142
|
+
while IFS= read -r mdfile; do
|
|
143
|
+
perma="$(awk '
|
|
144
|
+
/^---$/ {fm = !fm; next}
|
|
145
|
+
fm && /^permalink:/ {sub(/^permalink:[ \t]*/, ""); gsub(/["'\'']/, ""); print; exit}
|
|
146
|
+
' "$mdfile")"
|
|
147
|
+
[[ -z "$perma" ]] && continue
|
|
148
|
+
((PERMA_TOTAL++)) || true
|
|
149
|
+
perma_clean="${perma#/}"
|
|
150
|
+
perma_clean="${perma_clean%/}"
|
|
151
|
+
if [[ -f "_site/${perma_clean}/index.html" || -f "_site/${perma_clean}.html" ]]; then
|
|
152
|
+
((PERMA_OK++)) || true
|
|
153
|
+
else
|
|
154
|
+
PERMA_MISSING+=("$perma")
|
|
155
|
+
fi
|
|
156
|
+
done < <(find . -type f -name '*.md' -not -path './_site/*' -not -path './vendor/*' -not -path './node_modules/*' 2>/dev/null)
|
|
157
|
+
|
|
158
|
+
# --- just-the-docs navigation consistency ---
|
|
159
|
+
NAV_PARENTS=0
|
|
160
|
+
NAV_CHILDREN=0
|
|
161
|
+
NAV_ORPHANS=()
|
|
162
|
+
if [[ "$THEME" == "just-the-docs" ]]; then
|
|
163
|
+
declare -A PARENT_TITLES=()
|
|
164
|
+
while IFS= read -r mdfile; do
|
|
165
|
+
has_kids="$(awk '/^---$/{fm=!fm;next} fm && /^has_children:[ \t]*true/{print "yes";exit}' "$mdfile")"
|
|
166
|
+
title="$(awk '/^---$/{fm=!fm;next} fm && /^title:/{sub(/^title:[ \t]*/,"");gsub(/["'\'']/,"");print;exit}' "$mdfile")"
|
|
167
|
+
if [[ "$has_kids" == "yes" && -n "$title" ]]; then
|
|
168
|
+
PARENT_TITLES["$title"]=1
|
|
169
|
+
((NAV_PARENTS++)) || true
|
|
170
|
+
fi
|
|
171
|
+
done < <(find . -type f -name '*.md' -not -path './_site/*' -not -path './vendor/*' 2>/dev/null)
|
|
172
|
+
|
|
173
|
+
while IFS= read -r mdfile; do
|
|
174
|
+
parent="$(awk '/^---$/{fm=!fm;next} fm && /^parent:/{sub(/^parent:[ \t]*/,"");gsub(/["'\'']/,"");print;exit}' "$mdfile")"
|
|
175
|
+
[[ -z "$parent" ]] && continue
|
|
176
|
+
((NAV_CHILDREN++)) || true
|
|
177
|
+
if [[ -z "${PARENT_TITLES[$parent]:-}" ]]; then
|
|
178
|
+
NAV_ORPHANS+=("$mdfile -> parent:$parent")
|
|
179
|
+
fi
|
|
180
|
+
done < <(find . -type f -name '*.md' -not -path './_site/*' -not -path './vendor/*' 2>/dev/null)
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
# --- Custom includes ---
|
|
184
|
+
INC_FOOTER="-"
|
|
185
|
+
INC_HEAD="-"
|
|
186
|
+
if [[ -f _includes/footer_custom.html ]]; then
|
|
187
|
+
if grep -qF "$(head -3 _includes/footer_custom.html)" _site/index.html 2>/dev/null; then
|
|
188
|
+
INC_FOOTER="ok"
|
|
189
|
+
else
|
|
190
|
+
INC_FOOTER="missing"
|
|
191
|
+
fi
|
|
192
|
+
fi
|
|
193
|
+
if [[ -f _includes/head_custom.html ]]; then
|
|
194
|
+
if grep -qF "$(head -3 _includes/head_custom.html)" _site/index.html 2>/dev/null; then
|
|
195
|
+
INC_HEAD="ok"
|
|
196
|
+
else
|
|
197
|
+
INC_HEAD="missing"
|
|
198
|
+
fi
|
|
199
|
+
fi
|
|
200
|
+
|
|
201
|
+
# --- Summary ---
|
|
202
|
+
echo
|
|
203
|
+
echo "Jekyll Site Test Results"
|
|
204
|
+
echo "========================"
|
|
205
|
+
echo "Theme: ${THEME:-<none>}"
|
|
206
|
+
echo "Color scheme: ${COLOR_SCHEME:-<none>}"
|
|
207
|
+
echo "Build: OK ${BUILD_TIME:+($BUILD_TIME)}"
|
|
208
|
+
if [[ -n "$COLOR_SCHEME" && "$COLOR_TOTAL" -gt 0 ]]; then
|
|
209
|
+
echo "Colors: $COLOR_FOUND/$COLOR_TOTAL custom colors found in CSS"
|
|
210
|
+
fi
|
|
211
|
+
echo "Pages: $PERMA_OK/$PERMA_TOTAL permalinked pages built"
|
|
212
|
+
if [[ "$THEME" == "just-the-docs" ]]; then
|
|
213
|
+
echo "Navigation: $NAV_PARENTS parents, $NAV_CHILDREN children, ${#NAV_ORPHANS[@]} orphans"
|
|
214
|
+
fi
|
|
215
|
+
echo "Includes: footer=$INC_FOOTER, head=$INC_HEAD"
|
|
216
|
+
|
|
217
|
+
# --- Failure detail (reported, not fatal) ---
|
|
218
|
+
if [[ ${#COLOR_MISSING[@]} -gt 0 ]]; then
|
|
219
|
+
echo
|
|
220
|
+
echo "Missing colors:"
|
|
221
|
+
printf ' %s\n' "${COLOR_MISSING[@]}"
|
|
222
|
+
fi
|
|
223
|
+
if [[ ${#PERMA_MISSING[@]} -gt 0 ]]; then
|
|
224
|
+
echo
|
|
225
|
+
echo "Missing permalinked pages:"
|
|
226
|
+
printf ' %s\n' "${PERMA_MISSING[@]}"
|
|
227
|
+
fi
|
|
228
|
+
if [[ ${#NAV_ORPHANS[@]} -gt 0 ]]; then
|
|
229
|
+
echo
|
|
230
|
+
echo "Orphan nav children (parent not found):"
|
|
231
|
+
printf ' %s\n' "${NAV_ORPHANS[@]}"
|
|
232
|
+
fi
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: notebooklm
|
|
3
|
+
description: >
|
|
4
|
+
Generate GitHub links to repo documentation for NotebookLM ingestion.
|
|
5
|
+
Use when: the user wants to create a NotebookLM notebook about a project,
|
|
6
|
+
needs doc links for a repo, or says "notebooklm", "notebook sources",
|
|
7
|
+
"get repo sources", or "doc links".
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# NotebookLM — Repo Source Links
|
|
11
|
+
|
|
12
|
+
Generate GitHub blob URLs for all documentation in the current repo, ready to paste into Google NotebookLM as sources.
|
|
13
|
+
|
|
14
|
+
## When to Use
|
|
15
|
+
|
|
16
|
+
- Creating a NotebookLM notebook about a project
|
|
17
|
+
- Gathering all doc links for a repo
|
|
18
|
+
- Exporting documentation URLs for any external tool
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Categorized output (default)
|
|
24
|
+
bash .claude/skills/notebooklm/scripts/get-repo-sources.sh
|
|
25
|
+
|
|
26
|
+
# Plain URLs only (for copy-paste)
|
|
27
|
+
bash .claude/skills/notebooklm/scripts/get-repo-sources.sh --plain
|
|
28
|
+
|
|
29
|
+
# Include plans and specs
|
|
30
|
+
bash .claude/skills/notebooklm/scripts/get-repo-sources.sh --all
|
|
31
|
+
|
|
32
|
+
# Override branch
|
|
33
|
+
bash .claude/skills/notebooklm/scripts/get-repo-sources.sh --branch develop
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Options
|
|
37
|
+
|
|
38
|
+
| Flag | Default | Description |
|
|
39
|
+
|------|---------|-------------|
|
|
40
|
+
| `--all` | off | Include plans, specs, and changelogs |
|
|
41
|
+
| `--plain` | off | Output only URLs, one per line (no headers) |
|
|
42
|
+
| `--branch NAME` | auto-detect | Override the git branch used in URLs |
|
|
43
|
+
|
|
44
|
+
## Requirements
|
|
45
|
+
|
|
46
|
+
- Must be run inside a git repository with a GitHub remote
|
|
47
|
+
- `git` CLI available
|