harness: fix py-vs-python3 doc gap — add py.sh resolver, repoint skill/command docs
The skill/command DOCS instructed Claude to run a bare `py ...`, which is the Windows py-launcher — absent on Linux/macOS (exit 127, hit on GURU-KALI). A blind py->python3 swap is wrong too: python3 is a broken MS Store shim on some Windows boxes where `py` is the correct launcher. Fix mirrors the resolution the .sh skill scripts already do: - New .claude/scripts/py.sh: picks the interpreter that actually RUNS — identity.json python.command first, then py -> python3 -> python, each validated with `-c 'import sys'` so the MS Store stub is skipped. exec's it. - Repointed all DOC invocations (10 files, ~70 sites) from `py ...` to `bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ...` (incl. the `py -c` and `py -` heredoc forms in checkpoint.md / mailbox.md). - Left the .sh skill scripts untouched — they already resolve py/python/python3. - errorlog.md: marked the GURU-KALI entry RESOLVED. Depends on CLAUDETOOLS_ROOT (seeded by ensure-settings-env.py); py.sh also self-resolves the repo root via git/cwd as a fallback. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -33,7 +33,7 @@ Please create a comprehensive git checkpoint with the following steps:
|
|||||||
|
|
||||||
# Ollama drafts the body; fallback to Claude if unavailable
|
# Ollama drafts the body; fallback to Claude if unavailable
|
||||||
if [ -n "$OLLAMA" ]; then
|
if [ -n "$OLLAMA" ]; then
|
||||||
BODY=$(py -c "
|
BODY=$(bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" -c "
|
||||||
import urllib.request, json
|
import urllib.request, json
|
||||||
diff = open('C:/Users/guru/AppData/Local/Temp/checkpoint_diff.txt', encoding='utf-8').read()
|
diff = open('C:/Users/guru/AppData/Local/Temp/checkpoint_diff.txt', encoding='utf-8').read()
|
||||||
prompt = 'Write a git commit message BODY only (not the summary line). Imperative mood. What changed and why. No filler. Under 150 words.\n\nDIFF:\n' + diff
|
prompt = 'Write a git commit message BODY only (not the summary line). Imperative mood. What changed and why. No filler. Under 150 words.\n\nDIFF:\n' + diff
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ VAULT="$REPO_ROOT/.claude/scripts/vault.sh"
|
|||||||
All Graph calls go through this `py` helper. It reads the secret from the vault, caches the token, and exposes `get`/`post`. Reuse the pattern per command.
|
All Graph calls go through this `py` helper. It reads the secret from the vault, caches the token, and exposes `get`/`post`. Reuse the pattern per command.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
py - "$MAILBOX" "$1" <<'PY'
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" - "$MAILBOX" "$1" <<'PY'
|
||||||
import os, sys, json, time, subprocess, urllib.request, urllib.parse, re
|
import os, sys, json, time, subprocess, urllib.request, urllib.parse, re
|
||||||
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
|
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
|
||||||
MAILBOX = sys.argv[1]
|
MAILBOX = sys.argv[1]
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ Reconstruct a session log from a Claude Code or Grok transcript when a session c
|
|||||||
Run the detector in scan-only mode and present the table to the user:
|
Run the detector in scan-only mode and present the table to the user:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
py .claude/scripts/detect_orphaned_sessions.py --dry-run
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" .claude/scripts/detect_orphaned_sessions.py --dry-run
|
||||||
```
|
```
|
||||||
|
|
||||||
The table shows every past-idle, not-yet-processed transcript with its uuid, mtime, `substantive`/`saved`/`orphan` verdicts, classified scope, and the path a recovery would write to. Point the user at the rows where `orphan` is `YES` — those are unsaved substantive sessions. Nothing is written.
|
The table shows every past-idle, not-yet-processed transcript with its uuid, mtime, `substantive`/`saved`/`orphan` verdicts, classified scope, and the path a recovery would write to. Point the user at the rows where `orphan` is `YES` — those are unsaved substantive sessions. Nothing is written.
|
||||||
@@ -36,7 +36,7 @@ This is a **reviewed** recovery. Claude is the editor, not a passive writer.
|
|||||||
1. **Generate the draft** (prints to stdout, writes nothing):
|
1. **Generate the draft** (prints to stdout, writes nothing):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
py .claude/scripts/recover_session.py --uuid <uuid> --print
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" .claude/scripts/recover_session.py --uuid <uuid> --print
|
||||||
```
|
```
|
||||||
|
|
||||||
(or `--latest`). The draft contains:
|
(or `--latest`). The draft contains:
|
||||||
@@ -103,6 +103,6 @@ In `/recover` flows, if the chosen orphan or explicit id lives under a Grok sess
|
|||||||
|
|
||||||
Example (manual):
|
Example (manual):
|
||||||
```bash
|
```bash
|
||||||
py .claude/scripts/recover_grok_session.py --latest --print
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" .claude/scripts/recover_grok_session.py --latest --print
|
||||||
py .claude/scripts/recover_grok_session.py --uuid 019e8b67-f97e-7b33-9c45-ec34b342d3eb --auto
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" .claude/scripts/recover_grok_session.py --uuid 019e8b67-f97e-7b33-9c45-ec34b342d3eb --auto
|
||||||
```
|
```
|
||||||
|
|||||||
31
.claude/scripts/py.sh
Executable file
31
.claude/scripts/py.sh
Executable file
@@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Purpose: Resolve the Python interpreter that actually WORKS on this machine and exec it
|
||||||
|
# with the given args. Skill/command docs call this instead of a bare `py` so the
|
||||||
|
# same documented command runs everywhere.
|
||||||
|
# Why: `py` is the Windows py-launcher (absent on Linux/macOS -> exit 127), while
|
||||||
|
# `python3` is a broken MS Store shim on some Windows boxes. Neither literal is
|
||||||
|
# portable. This picks the first interpreter that genuinely executes.
|
||||||
|
# Usage: bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" <script.py> [args...]
|
||||||
|
# (mirrors the py/python/python3 fallback already used inside the .sh skill scripts)
|
||||||
|
# Origin: added 2026-06-14 to close the py-vs-python3 doc gap (errorlog.md, GURU-KALI).
|
||||||
|
set -u
|
||||||
|
|
||||||
|
# A candidate is only valid if it RUNS — this skips the MS Store python3 stub, which
|
||||||
|
# exists on PATH but exits non-zero (prompting a store install) instead of executing.
|
||||||
|
_works() { command -v "$1" >/dev/null 2>&1 && "$1" -c 'import sys' >/dev/null 2>&1; }
|
||||||
|
|
||||||
|
PY=""
|
||||||
|
# 1) Honor identity.json's declared command first (authoritative per-machine), if it works.
|
||||||
|
ROOT="${CLAUDETOOLS_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"
|
||||||
|
ID="$ROOT/.claude/identity.json"
|
||||||
|
if [ -f "$ID" ]; then
|
||||||
|
cand=$(sed -n 's/.*"command"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$ID" | head -1)
|
||||||
|
[ -n "$cand" ] && _works "$cand" && PY="$cand"
|
||||||
|
fi
|
||||||
|
# 2) Fall back to autodetect: py (Win launcher) -> python3 -> python, first that works.
|
||||||
|
if [ -z "$PY" ]; then
|
||||||
|
for c in py python3 python; do _works "$c" && { PY="$c"; break; }; done
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -z "$PY" ] && { echo "[py.sh] no working python found (tried identity.json, py, python3, python)" >&2; exit 127; }
|
||||||
|
exec "$PY" "$@"
|
||||||
@@ -25,9 +25,9 @@ work with `python`/`python3`.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# from the scripts dir, or pass full paths
|
# from the scripts dir, or pass full paths
|
||||||
py "$CLAUDETOOLS_ROOT/.claude/skills/b2/scripts/b2.py" status
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" "$CLAUDETOOLS_ROOT/.claude/skills/b2/scripts/b2.py" status
|
||||||
py "$CLAUDETOOLS_ROOT/.claude/skills/b2/scripts/b2.py" buckets
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" "$CLAUDETOOLS_ROOT/.claude/skills/b2/scripts/b2.py" buckets
|
||||||
py "$CLAUDETOOLS_ROOT/.claude/skills/b2/scripts/b2.py" usage --json
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" "$CLAUDETOOLS_ROOT/.claude/skills/b2/scripts/b2.py" usage --json
|
||||||
```
|
```
|
||||||
|
|
||||||
Transport auto-selects: uses `httpx` if installed, otherwise stdlib `urllib`
|
Transport auto-selects: uses `httpx` if installed, otherwise stdlib `urllib`
|
||||||
@@ -95,7 +95,7 @@ into tickets/logs. Destructive calls are NEVER retried automatically.
|
|||||||
## Common commands
|
## Common commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
B2="py $CLAUDETOOLS_ROOT/.claude/skills/b2/scripts/b2.py"
|
B2="bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" $CLAUDETOOLS_ROOT/.claude/skills/b2/scripts/b2.py"
|
||||||
|
|
||||||
# Status / inventory
|
# Status / inventory
|
||||||
$B2 status
|
$B2 status
|
||||||
@@ -175,7 +175,7 @@ that would exceed that.
|
|||||||
### Commands
|
### Commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
B2="py $CLAUDETOOLS_ROOT/.claude/skills/b2/scripts/b2.py"
|
B2="bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" $CLAUDETOOLS_ROOT/.claude/skills/b2/scripts/b2.py"
|
||||||
|
|
||||||
# 1. READ-ONLY: see the bucket's current lifecycle rules + revision
|
# 1. READ-ONLY: see the bucket's current lifecycle rules + revision
|
||||||
$B2 lifecycle ACG-Internal
|
$B2 lifecycle ACG-Internal
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ work with `python`/`python3`.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# from the scripts dir, or pass full paths
|
# from the scripts dir, or pass full paths
|
||||||
py "C:/claudetools/.claude/skills/bitdefender/scripts/gz.py" status
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" "C:/claudetools/.claude/skills/bitdefender/scripts/gz.py" status
|
||||||
py "C:/claudetools/.claude/skills/bitdefender/scripts/gz.py" companies
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" "C:/claudetools/.claude/skills/bitdefender/scripts/gz.py" companies
|
||||||
py "C:/claudetools/.claude/skills/bitdefender/scripts/gz.py" sweep --company <id> --json
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" "C:/claudetools/.claude/skills/bitdefender/scripts/gz.py" sweep --company <id> --json
|
||||||
```
|
```
|
||||||
|
|
||||||
Transport auto-selects: uses `httpx` if installed, otherwise stdlib `urllib`
|
Transport auto-selects: uses `httpx` if installed, otherwise stdlib `urllib`
|
||||||
@@ -105,7 +105,7 @@ raw output into tickets/logs without review.
|
|||||||
## Common commands
|
## Common commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
GZ="py C:/claudetools/.claude/skills/bitdefender/scripts/gz.py"
|
GZ="bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" C:/claudetools/.claude/skills/bitdefender/scripts/gz.py"
|
||||||
|
|
||||||
# Status / inventory
|
# Status / inventory
|
||||||
$GZ status
|
$GZ status
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ and the `user`/`machine` for attribution) and bakes in the fleet conventions, so
|
|||||||
never hand-build the JSON again.
|
never hand-build the JSON again.
|
||||||
|
|
||||||
```
|
```
|
||||||
py "$CLAUDETOOLS_ROOT/.claude/skills/coord/scripts/coord.py" <command> ...
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" "$CLAUDETOOLS_ROOT/.claude/skills/coord/scripts/coord.py" <command> ...
|
||||||
```
|
```
|
||||||
|
|
||||||
| Command | What it does |
|
| Command | What it does |
|
||||||
|
|||||||
@@ -57,17 +57,17 @@ also work. Run from the scripts dir so the two modules resolve.
|
|||||||
```bash
|
```bash
|
||||||
cd C:/claudetools/.claude/skills/mailprotector/scripts
|
cd C:/claudetools/.claude/skills/mailprotector/scripts
|
||||||
|
|
||||||
py mp.py status # validate token (GET /domains, per_page=1)
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py status # validate token (GET /domains, per_page=1)
|
||||||
py mp.py domains # list domains (global)
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py domains # list domains (global)
|
||||||
py mp.py domains --scope customers --id <id>
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py domains --scope customers --id <id>
|
||||||
py mp.py domain <domain_id>
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py domain <domain_id>
|
||||||
py mp.py customers <reseller_id>
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py customers <reseller_id>
|
||||||
py mp.py customer <customer_id>
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py customer <customer_id>
|
||||||
py mp.py users <scope> <id>
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py users <scope> <id>
|
||||||
py mp.py user <user_id>
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py user <user_id>
|
||||||
py mp.py find-user user@client.com # locate a user / alias by email (a READ)
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py find-user user@client.com # locate a user / alias by email (a READ)
|
||||||
py mp.py config <scope> <id> # shows permissions.messages.allow_spam_release
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py config <scope> <id> # shows permissions.messages.allow_spam_release
|
||||||
py mp.py rules <scope> <id>
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py rules <scope> <id>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Mail-flow logs and held mail (the common investigation)
|
### Mail-flow logs and held mail (the common investigation)
|
||||||
@@ -77,10 +77,10 @@ Both accept the same filters: `--sender --recipient --subject --decision
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Why didn't this arrive? Look at the decision in the flow logs.
|
# Why didn't this arrive? Look at the decision in the flow logs.
|
||||||
py mp.py logs domains <domain_id> --recipient ceo@client.com --decision quarantine_spam
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py logs domains <domain_id> --recipient ceo@client.com --decision quarantine_spam
|
||||||
|
|
||||||
# Held / quarantined mail search.
|
# Held / quarantined mail search.
|
||||||
py mp.py messages domains <domain_id> --sender boss@vendor.com
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py messages domains <domain_id> --sender boss@vendor.com
|
||||||
```
|
```
|
||||||
|
|
||||||
`--decision` values: `default`, `deliver`, `quarantine_spam`,
|
`--decision` values: `default`, `deliver`, `quarantine_spam`,
|
||||||
@@ -95,12 +95,12 @@ Every mutating command prints a `[DRY RUN]` line and exits non-zero unless you
|
|||||||
pass `--confirm`.
|
pass `--confirm`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
py mp.py release <message_id> --confirm
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py release <message_id> --confirm
|
||||||
py mp.py release <message_id> --recipients alt@client.com --confirm
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py release <message_id> --recipients alt@client.com --confirm
|
||||||
py mp.py release-many <scope> <id> --ids 111,222,333 --confirm
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py release-many <scope> <id> --ids 111,222,333 --confirm
|
||||||
py mp.py release-many <scope> <id> --all --confirm
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py release-many <scope> <id> --all --confirm
|
||||||
py mp.py add-rule <scope> <id> --value vendor.com --type allow --confirm
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py add-rule <scope> <id> --value vendor.com --type allow --confirm
|
||||||
py mp.py enable-release <scope> <id> --confirm
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py enable-release <scope> <id> --confirm
|
||||||
```
|
```
|
||||||
|
|
||||||
## The `allow_spam_release` gotcha
|
## The `allow_spam_release` gotcha
|
||||||
@@ -108,8 +108,8 @@ py mp.py enable-release <scope> <id> --confirm
|
|||||||
Releasing a held **spam** message fails if the owning entity does not have
|
Releasing a held **spam** message fails if the owning entity does not have
|
||||||
`permissions.messages.allow_spam_release = true`. Workflow:
|
`permissions.messages.allow_spam_release = true`. Workflow:
|
||||||
|
|
||||||
1. `py mp.py config <scope> <id>` — check `allow_spam_release`.
|
1. `bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py config <scope> <id>` — check `allow_spam_release`.
|
||||||
2. If `false`: `py mp.py enable-release <scope> <id> --confirm`.
|
2. If `false`: `bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py enable-release <scope> <id> --confirm`.
|
||||||
3. Re-run the `release` / `release-many`.
|
3. Re-run the `release` / `release-many`.
|
||||||
|
|
||||||
Virus and policy quarantines are governed separately — only spam release is
|
Virus and policy quarantines are governed separately — only spam release is
|
||||||
@@ -119,18 +119,18 @@ gated by this permission.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Find the client's domain.
|
# 1. Find the client's domain.
|
||||||
py mp.py domains --scope customers --id <customer_id>
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py domains --scope customers --id <customer_id>
|
||||||
|
|
||||||
# 2. Search held messages from the sender (outbound = sender is the client user).
|
# 2. Search held messages from the sender (outbound = sender is the client user).
|
||||||
py mp.py messages domains <domain_id> --sender user@client.com --decision quarantine_spam
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py messages domains <domain_id> --sender user@client.com --decision quarantine_spam
|
||||||
|
|
||||||
# 3. If it's spam-held, make sure release is permitted on the domain.
|
# 3. If it's spam-held, make sure release is permitted on the domain.
|
||||||
py mp.py config domains <domain_id> # check allow_spam_release
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py config domains <domain_id> # check allow_spam_release
|
||||||
py mp.py enable-release domains <domain_id> --confirm # only if needed
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py enable-release domains <domain_id> --confirm # only if needed
|
||||||
|
|
||||||
# 4. Release by message id (DRY RUN first — omit --confirm to preview).
|
# 4. Release by message id (DRY RUN first — omit --confirm to preview).
|
||||||
py mp.py release <message_id> # [DRY RUN]
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py release <message_id> # [DRY RUN]
|
||||||
py mp.py release <message_id> --confirm # actually release
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py release <message_id> --confirm # actually release
|
||||||
```
|
```
|
||||||
|
|
||||||
## Raw escape hatch
|
## Raw escape hatch
|
||||||
@@ -139,8 +139,8 @@ The named commands cover the common surface; for anything else, hit the path
|
|||||||
directly. Non-GET methods still require `--confirm`.
|
directly. Non-GET methods still require `--confirm`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
py mp.py raw GET domains/<id>/logs
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py raw GET domains/<id>/logs
|
||||||
py mp.py raw POST messages/<id>/deliver --body '{"include_original_recipients":1}' --confirm
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py raw POST messages/<id>/deliver --body '{"include_original_recipients":1}' --confirm
|
||||||
```
|
```
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ state first with `config <scope> <id>` and look at
|
|||||||
## Raw escape hatch
|
## Raw escape hatch
|
||||||
|
|
||||||
```
|
```
|
||||||
py mp.py raw <METHOD> <path> [--body JSON] [--confirm]
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py raw <METHOD> <path> [--body JSON] [--confirm]
|
||||||
```
|
```
|
||||||
Non-GET methods require `--confirm`. Use for any endpoint not wrapped by a named
|
Non-GET methods require `--confirm`. Use for any endpoint not wrapped by a named
|
||||||
command.
|
command.
|
||||||
@@ -147,8 +147,8 @@ command.
|
|||||||
Releasing a held **spam** message will fail (or silently no-op) if the owning
|
Releasing a held **spam** message will fail (or silently no-op) if the owning
|
||||||
entity does not have `permissions.messages.allow_spam_release = true`. The fix:
|
entity does not have `permissions.messages.allow_spam_release = true`. The fix:
|
||||||
|
|
||||||
1. `py mp.py config <scope> <id>` — confirm `allow_spam_release` is `false`.
|
1. `bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py config <scope> <id>` — confirm `allow_spam_release` is `false`.
|
||||||
2. `py mp.py enable-release <scope> <id> --confirm` — flip it to `true`.
|
2. `bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" mp.py enable-release <scope> <id> --confirm` — flip it to `true`.
|
||||||
3. Re-run the `release` / `release-many`.
|
3. Re-run the `release` / `release-many`.
|
||||||
|
|
||||||
Virus and policy quarantines are governed separately — only spam release is
|
Virus and policy quarantines are governed separately — only spam release is
|
||||||
|
|||||||
@@ -99,13 +99,13 @@ runs under `python` / `python3`. Stdlib only -- no pip deps.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# REPORT ONLY (default) -- writes _reports/<stamp>-dream.md and prints it
|
# REPORT ONLY (default) -- writes _reports/<stamp>-dream.md and prints it
|
||||||
py "$CLAUDETOOLS_ROOT/.claude/skills/memory-dream/scripts/memory_dream.py"
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" "$CLAUDETOOLS_ROOT/.claude/skills/memory-dream/scripts/memory_dream.py"
|
||||||
|
|
||||||
# report to stdout only, write nothing
|
# report to stdout only, write nothing
|
||||||
py "$CLAUDETOOLS_ROOT/.claude/skills/memory-dream/scripts/memory_dream.py" --no-file
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" "$CLAUDETOOLS_ROOT/.claude/skills/memory-dream/scripts/memory_dream.py" --no-file
|
||||||
|
|
||||||
# additive-only fixes (append orphan index lines, migrate profile-only files)
|
# additive-only fixes (append orphan index lines, migrate profile-only files)
|
||||||
py "$CLAUDETOOLS_ROOT/.claude/skills/memory-dream/scripts/memory_dream.py" --apply-safe
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" "$CLAUDETOOLS_ROOT/.claude/skills/memory-dream/scripts/memory_dream.py" --apply-safe
|
||||||
```
|
```
|
||||||
|
|
||||||
`CLAUDETOOLS_ROOT` resolves from the env var, else `claudetools_root` in
|
`CLAUDETOOLS_ROOT` resolves from the env var, else `claudetools_root` in
|
||||||
@@ -141,5 +141,5 @@ of new files; no deletions, no merges, no overwrites of differing content).
|
|||||||
Run:
|
Run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
py "$CLAUDETOOLS_ROOT/.claude/skills/memory-dream/scripts/selftest.py"
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" "$CLAUDETOOLS_ROOT/.claude/skills/memory-dream/scripts/selftest.py"
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -61,16 +61,16 @@ also work. Run from the scripts dir so the two modules resolve.
|
|||||||
```bash
|
```bash
|
||||||
cd C:/claudetools/.claude/skills/packetdial/scripts
|
cd C:/claudetools/.claude/skills/packetdial/scripts
|
||||||
|
|
||||||
py ns.py status # API version + authenticated key identity
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py status # API version + authenticated key identity
|
||||||
py ns.py domains # list all domains
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py domains # list all domains
|
||||||
py ns.py domain <domain> # one domain's config
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py domain <domain> # one domain's config
|
||||||
py ns.py users <domain> # users / extensions in a domain
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py users <domain> # users / extensions in a domain
|
||||||
py ns.py user <domain> <user>
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py user <domain> <user>
|
||||||
py ns.py phones <domain> # SIP devices registered in a domain
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py phones <domain> # SIP devices registered in a domain
|
||||||
py ns.py dids <domain> # phone numbers (DIDs) on a domain
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py dids <domain> # phone numbers (DIDs) on a domain
|
||||||
py ns.py devices <domain> <user>
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py devices <domain> <user>
|
||||||
py ns.py cdrs --domain <domain> --start 2026-06-01 --end 2026-06-02
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py cdrs --domain <domain> --start 2026-06-01 --end 2026-06-02
|
||||||
py ns.py resellers
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py resellers
|
||||||
```
|
```
|
||||||
|
|
||||||
## Writes (gated)
|
## Writes (gated)
|
||||||
@@ -79,12 +79,12 @@ Every mutating command prints a `[DRY RUN]` line and exits non-zero unless you
|
|||||||
pass `--confirm`. Bodies are raw JSON matching the NetSapiens v2 schema.
|
pass `--confirm`. Bodies are raw JSON matching the NetSapiens v2 schema.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
py ns.py create-domain --body '{"domain":"acme","description":"Acme Inc"}' --confirm
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py create-domain --body '{"domain":"acme","description":"Acme Inc"}' --confirm
|
||||||
py ns.py create-user acme --body '{"user":"101","name-first-name":"Jane"}' --confirm
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py create-user acme --body '{"user":"101","name-first-name":"Jane"}' --confirm
|
||||||
py ns.py create-phone acme --body '{...}' --confirm
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py create-phone acme --body '{...}' --confirm
|
||||||
py ns.py create-did acme --body '{"phonenumber":"15205551234"}' --confirm
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py create-did acme --body '{"phonenumber":"15205551234"}' --confirm
|
||||||
py ns.py update-user acme 101 --body '{"name-last-name":"Doe"}' --confirm
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py update-user acme 101 --body '{"name-last-name":"Doe"}' --confirm
|
||||||
py ns.py delete-user acme 101 --confirm
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py delete-user acme 101 --confirm
|
||||||
```
|
```
|
||||||
|
|
||||||
## Raw escape hatch (any of the 239 v2 paths)
|
## Raw escape hatch (any of the 239 v2 paths)
|
||||||
@@ -93,8 +93,8 @@ The named commands cover the common surface; for anything else, hit the path
|
|||||||
directly. Non-GET methods still require `--confirm`.
|
directly. Non-GET methods still require `--confirm`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
py ns.py raw GET domains/acme/users/101/answerrules
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py raw GET domains/acme/users/101/answerrules
|
||||||
py ns.py raw POST domains/acme/users --body '{...}' --confirm
|
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ns.py raw POST domains/acme/users --body '{...}' --confirm
|
||||||
```
|
```
|
||||||
|
|
||||||
## Standard provisioning flow (new customer)
|
## Standard provisioning flow (new customer)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Format: `YYYY-MM-DD | MACHINE | command/skill | error (brief)`
|
|||||||
|
|
||||||
<!-- Append entries below this line -->
|
<!-- Append entries below this line -->
|
||||||
|
|
||||||
2026-06-14 | GURU-KALI | coord skill (coord.py) | Documented invocation `py .claude/skills/coord/scripts/coord.py ...` failed exit 127 — `py` (the Windows py-launcher) does not exist on Linux. Worked around with `python3`. Skill docs assume the `py` launcher; on Linux/macOS machines they need `python3`. Candidate: skill docs use a platform-neutral python resolver, or document `python3` for non-Windows.
|
2026-06-14 | GURU-KALI | coord skill (coord.py) | Documented invocation `py .claude/skills/coord/scripts/coord.py ...` failed exit 127 — `py` (the Windows py-launcher) does not exist on Linux. Worked around with `python3`. [RESOLVED 2026-06-14] Added `.claude/scripts/py.sh` (resolves the working interpreter: identity.json `python.command` -> py -> python3 -> python, skipping the MS Store shim) and repointed all skill/command DOC invocations from bare `py` to `bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh"`. The `.sh` skill scripts already resolved internally — left untouched. Broadcast to fleet.
|
||||||
|
|
||||||
2026-06-14 | GURU-BEAST-ROG | coord skill (coord.py msg send) | `py "$CLAUDETOOLS_ROOT/.claude/skills/coord/scripts/coord.py"` failed — `$CLAUDETOOLS_ROOT` is not exported in fresh Git-bash shells here, so the path resolved under `C:\Program Files\Git\`. [RESOLVED 2026-06-14] Added `.claude/scripts/ensure-settings-env.py` (seeds `env.CLAUDETOOLS_ROOT` in per-machine `settings.local.json` from `identity.json`); Claude Code injects it into every Bash call. Wired into ONBOARDING.md + broadcast to fleet. Effective next session start.
|
2026-06-14 | GURU-BEAST-ROG | coord skill (coord.py msg send) | `py "$CLAUDETOOLS_ROOT/.claude/skills/coord/scripts/coord.py"` failed — `$CLAUDETOOLS_ROOT` is not exported in fresh Git-bash shells here, so the path resolved under `C:\Program Files\Git\`. [RESOLVED 2026-06-14] Added `.claude/scripts/ensure-settings-env.py` (seeds `env.CLAUDETOOLS_ROOT` in per-machine `settings.local.json` from `identity.json`); Claude Code injects it into every Bash call. Wired into ONBOARDING.md + broadcast to fleet. Effective next session start.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user