Compare commits

..

30 Commits

Author SHA1 Message Date
clawdbot-ci
c94e18bc15 update tool releases 2026-05-04 03:05:05 +00:00
clawdbot-ci
620dc9666c update tool releases 2026-05-03 15:44:43 +00:00
Peter Steinberger
433c7b06af
fix: match peekaboo arm64 release asset 2026-05-03 16:40:35 +01:00
clawdbot-ci
de56f637f2 update tool releases 2026-04-27 07:17:00 +00:00
Dave Dennis
3103831ea1
feat: add CodexBar app package and module
Thanks @0xdsqr for the contribution.

Co-authored-by: 0xdsqr <me@dsqr.dev>
2026-04-27 05:57:45 +01:00
clawdbot-ci
1d8fe51b1e update tool releases 2026-04-26 06:02:42 +00:00
clawdbot-ci
81cbbedb53 update tool releases 2026-04-26 02:49:32 +00:00
Dave Dennis
26a27195ca
feat: add crawling tool plugins
Adds discrawl and wacrawl packages, plugin flakes, skills, updater wiring, and README entries.
2026-04-26 03:16:51 +01:00
Dan Sanduleac
3584e2b6d2
chore: add summarize subflake lock
Adds the committed lock file from PR #8.
2026-04-26 03:04:42 +01:00
Peter Steinberger
2afbbd55bf
fix: disable broken bird package export 2026-04-26 03:03:55 +01:00
Peter Steinberger
457023efd4
fix: resolve package build blockers 2026-04-26 03:02:50 +01:00
clawdbot-ci
3e8e39bd85 sync skills from clawdbot 2026-04-23 21:47:04 +00:00
clawdbot-ci
9647ce7fcb update tool releases 2026-04-22 18:22:21 +00:00
clawdbot-ci
65a58f8367 update tool releases 2026-04-20 22:27:49 +00:00
clawdbot-ci
d17f197c47 update tool releases 2026-04-08 00:02:19 +00:00
clawdbot-ci
5f677a283d sync skills from clawdbot 2026-03-23 04:19:03 +00:00
clawdbot-ci
cd4c429ff3 sync skills from clawdbot 2026-03-15 07:59:40 +00:00
clawdbot-ci
526067c585 update tool releases 2026-03-12 02:36:51 +00:00
clawdbot-ci
9392ba9ac6 sync skills from clawdbot 2026-03-11 14:26:05 +00:00
clawdbot-ci
561592b0b1 update tool releases 2026-03-09 06:43:03 +00:00
clawdbot-ci
2b97c49e03 update tool releases 2026-02-26 12:46:07 +00:00
joshp123
50194a9b8e fix(gogcli-plugin): repin tool flake root to openclaw rev with 0.11.1
Why
- tools/gogcli was pinned to an older root rev, so plugin output still resolved
  gogcli 0.9.0 even after package bump.

What
- update tools/gogcli root input to openclaw/nix-steipete-tools rev eddb00d...
- refresh tools/gogcli/flake.lock root input to same rev

Tests
- cd tools/gogcli && nix build .#gogcli
- cd tools/gogcli && ./result/bin/gog version  # 0.11.1
2026-02-26 13:42:18 +01:00
joshp123
eddb00d4c1 chore(gogcli): pin to joshp123 fork v0.11.1
Why
- Need immediate delivery of refresh-token rotation persistence fix before upstream release.
- Keep existing binary-tar packaging flow (no package-model changes).

What
- bump gogcli package version 0.11.0 -> 0.11.1
- switch release artifact URLs to joshp123/gogcli v0.11.1
- update SRI hashes for darwin arm64, linux amd64, linux arm64 assets

Tests
- nix build .#gogcli
- ./result/bin/gog version
2026-02-26 13:40:02 +01:00
clawdbot-ci
95ebfa73f4 sync skills from clawdbot 2026-02-21 02:00:17 +00:00
joshp123
c110209720 🤖 chore: add lock files for goplaces/gogcli plugin flakes
What:
- add tools/gogcli/flake.lock
- add tools/goplaces/flake.lock

Why:
- avoid ad-hoc lockfile mutation warnings when these plugin flakes are evaluated as inputs
- keep plugin flake inputs deterministic for downstream consumers

Tests:
- nix flake lock (tools/gogcli)
- nix flake lock (tools/goplaces)
2026-02-18 09:25:35 -08:00
joshp123
7cd25c98bb 🤖 refactor: remove oracle from nix-steipete-tools
What:
- delete oracle package and plugin exports
- remove oracle from tool update and skill sync pipelines
- remove oracle mentions from README

Why:
- oracle is no longer part of the bundled toolchain
- keep package/update surface aligned with active bundled plugins only

Tests:
- go test ./... (pass)
- nix flake check --no-build (pass)
2026-02-17 19:43:35 -08:00
clawdbot-ci
d5fc8e2b07 sync skills from clawdbot 2026-02-18 02:22:05 +00:00
clawdbot-ci
b668498d7d update tool releases 2026-02-16 06:57:14 +00:00
clawdbot-ci
90516869c1 update tool releases 2026-02-15 04:04:37 +00:00
joshp123
c718196c26 feat: package goplaces from steipete releases + auto-update 2026-02-14 19:16:00 -08:00
30 changed files with 928 additions and 482 deletions

View File

@ -16,21 +16,35 @@ These tools are essential for a capable openclaw instance - screen capture, came
- **Fresh**: CI keeps tools and skills at latest automatically
- **Integrated**: Skills teach your bot how to use each tool
## Pure Nix and Home Manager design
This repo is meant to be consumable directly from Nix flakes, Darwin, and Home
Manager configs. Packages should stay pinned and reproducible, and optional
Home Manager modules should expose normal `programs.<name>` options instead of
requiring downstream users to hand-write app bundle or launchd glue.
For macOS app bundles, prefer a Nix package plus Home Manager integration. Home
Manager can expose app bundles from `home.packages` through its Darwin app
targets, and modules can opt into `launchd.agents` when Home Manager should own
startup. Homebrew casks are still useful, but they belong in a nix-darwin
Homebrew configuration, not in these pure Nix package/module definitions.
## What's included
| Tool | What it does |
|------|--------------|
| [**summarize**](https://github.com/steipete/summarize) | Link → clean text → summary |
| [**discrawl**](https://github.com/steipete/discrawl) | Mirror Discord into SQLite and search history locally |
| [**wacrawl**](https://github.com/steipete/wacrawl) | Read-only local archive and search for WhatsApp Desktop data |
| [**gogcli**](https://github.com/steipete/gogcli) | Google CLI for Gmail, Calendar, Drive, and Contacts |
| [**goplaces**](https://github.com/steipete/goplaces) | Google Places API (New) CLI |
| [**camsnap**](https://github.com/steipete/camsnap) | Capture snapshots/clips from RTSP/ONVIF cameras |
| [**sonoscli**](https://github.com/steipete/sonoscli) | Control Sonos speakers |
| [**bird**](https://github.com/steipete/bird) | Fast X CLI for tweeting, replying, and reading |
| [**peekaboo**](https://github.com/steipete/peekaboo) | Lightning-fast macOS screenshots & AI vision analysis |
| [**poltergeist**](https://github.com/steipete/poltergeist) | Universal file watcher with auto-rebuild |
| [**sag**](https://github.com/steipete/sag) | Command-line ElevenLabs TTS with mac-style flags |
| [**imsg**](https://github.com/steipete/imsg) | iMessage/SMS CLI |
| [**oracle**](https://github.com/steipete/oracle) | Bundle prompts + files for AI queries |
| [**CodexBar**](https://github.com/steipete/CodexBar) | macOS menu bar app for Codex, Claude, and other provider usage |
## Usage (as openclaw plugins)
@ -39,8 +53,10 @@ Each tool is a subflake under `tools/<tool>/` exporting `openclawPlugin`. Point
```nix
programs.openclaw.plugins = [
{ source = "github:openclaw/nix-steipete-tools?dir=tools/camsnap"; }
{ source = "github:openclaw/nix-steipete-tools?dir=tools/discrawl"; }
{ source = "github:openclaw/nix-steipete-tools?dir=tools/peekaboo"; }
{ source = "github:openclaw/nix-steipete-tools?dir=tools/summarize"; }
{ source = "github:openclaw/nix-steipete-tools?dir=tools/wacrawl"; }
];
```
@ -58,13 +74,53 @@ inputs.nix-steipete-tools.url = "github:openclaw/nix-steipete-tools";
# Then use:
inputs.nix-steipete-tools.packages.aarch64-darwin.camsnap
inputs.nix-steipete-tools.packages.aarch64-darwin.discrawl
inputs.nix-steipete-tools.packages.aarch64-darwin.peekaboo
inputs.nix-steipete-tools.packages.aarch64-darwin.wacrawl
inputs.nix-steipete-tools.packages.aarch64-darwin.codexbar-app
# etc.
# Linux examples:
inputs.nix-steipete-tools.packages.x86_64-linux.camsnap
inputs.nix-steipete-tools.packages.x86_64-linux.discrawl
inputs.nix-steipete-tools.packages.aarch64-linux.gogcli
inputs.nix-steipete-tools.packages.x86_64-linux.summarize
inputs.nix-steipete-tools.packages.x86_64-linux.wacrawl
```
### Home Manager modules
Some packages also ship Home Manager modules so they can be enabled directly
without hand-writing install and launchd wiring. For CodexBar:
```nix
{
imports = [ inputs.nix-steipete-tools.homeManagerModules.codexbar ];
programs.codexbar.enable = true;
}
```
That adds the CodexBar app package to `home.packages`. Home Manager can then
expose the app bundle through its Darwin app targets. If you want Home Manager
to own startup too, enable the launchd agent explicitly:
```nix
{
programs.codexbar = {
enable = true;
launchd.enable = true;
launchd.keepAlive = false;
};
}
```
### macOS app bundles
If you only want the raw app package, use the package output directly:
```nix
inputs.nix-steipete-tools.packages.aarch64-darwin.codexbar-app
```
## Skills syncing
@ -85,7 +141,7 @@ Tools track upstream GitHub releases directly (not Homebrew).
go run ./cmd/update-tools
```
Fetches latest release versions/URLs/hashes and updates the Nix expressions. Oracle uses pnpm and auto-derives its hash via build mismatch.
Fetches latest release versions/URLs/hashes and updates the Nix expressions.
## CI
@ -97,6 +153,12 @@ Fetches latest release versions/URLs/hashes and updates the Nix expressions. Ora
Automated PRs keep everything fresh without manual intervention.
## Temporarily disabled
`bird` is not exported right now because the upstream GitHub release assets for
v0.8.0 are gone. The npm package still exists, but this flake does not currently
build npm dependencies for it.
## License
Tools are packaged as-is from upstream. See individual tool repos for their licenses.

View File

@ -60,14 +60,14 @@ func main() {
mappings := []Mapping{
{"summarize", "skills/summarize"},
{"discrawl", "skills/discrawl"},
{"wacrawl", "skills/wacrawl"},
{"gogcli", "skills/gog"},
{"camsnap", "skills/camsnap"},
{"sonoscli", "skills/sonoscli"},
{"bird", "skills/bird"},
{"peekaboo", "skills/peekaboo"},
{"sag", "skills/sag"},
{"imsg", "skills/imsg"},
{"oracle", "skills/oracle"},
}
log.Printf("[sync-skills] cloning clawdbot main")

View File

@ -140,85 +140,6 @@ func updateSummarize(repoRoot string) error {
return nil
}
func updateOracle(repoRoot string) error {
log.Printf("[update-tools] oracle")
oracleFile := filepath.Join(repoRoot, "nix", "pkgs", "oracle.nix")
orig, err := os.ReadFile(oracleFile)
if err != nil {
return err
}
rel, err := internal.LatestRelease("steipete/oracle")
if err != nil {
return err
}
version := strings.TrimPrefix(rel.TagName, "v")
var assetURL string
for _, a := range rel.Assets {
if matched, _ := regexp.MatchString(`oracle-[0-9.]+\.tgz`, a.Name); matched {
assetURL = a.BrowserDownloadURL
break
}
}
if assetURL == "" {
return fmt.Errorf("no asset matched for oracle")
}
assetHash, err := internal.PrefetchHash(assetURL)
if err != nil {
return err
}
lockHash, err := internal.PrefetchGitHub("steipete", "oracle", rel.TagName)
if err != nil {
return err
}
if err := internal.ReplaceOnce(oracleFile, regexp.MustCompile(`version = "[^"]+";`), fmt.Sprintf(`version = "%s";`, version)); err != nil {
return err
}
if err := internal.ReplaceOnce(oracleFile, regexp.MustCompile(`url = "[^"]+";`), fmt.Sprintf(`url = "%s";`, assetURL)); err != nil {
return err
}
if err := internal.ReplaceOnce(oracleFile, regexp.MustCompile(`hash = "sha256-[^"]+";`), fmt.Sprintf(`hash = "%s";`, assetHash)); err != nil {
return err
}
lockRe := regexp.MustCompile(`(?s)lockSrc = fetchFromGitHub \{[^}]*hash = "sha256-[^"]+";`)
if err := internal.ReplaceOnceFunc(oracleFile, lockRe, func(s string) string {
out := regexp.MustCompile(`rev = "[^"]+";`).ReplaceAllString(s, fmt.Sprintf(`rev = "%s";`, rel.TagName))
out = regexp.MustCompile(`hash = "sha256-[^"]+";`).ReplaceAllString(out, fmt.Sprintf(`hash = "%s";`, lockHash))
return out
}); err != nil {
return err
}
pnpmRe := regexp.MustCompile(`(?s)pnpmDeps.*hash = "sha256-[^"]+";`)
if err := internal.ReplaceOnceFunc(oracleFile, pnpmRe, func(s string) string {
return regexp.MustCompile(`hash = "sha256-[^"]+";`).ReplaceAllString(s, `hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";`)
}); err != nil {
return err
}
log.Printf("[update-tools] oracle: deriving pnpm hash")
logText, buildErr := internal.NixBuildOracle()
pnpmHash := internal.ExtractGotHash(logText)
if pnpmHash == "" {
// Restore original file so we don't leave a broken placeholder hash behind.
_ = os.WriteFile(oracleFile, orig, 0644)
// Surface some context in CI logs. This is usually a hash-mismatch line we failed to parse.
lines := strings.Split(logText, "\n")
start := 0
if len(lines) > 200 {
start = len(lines) - 200
}
log.Printf("[update-tools] oracle build output (last %d lines):\n%s", len(lines)-start, strings.Join(lines[start:], "\n"))
return fmt.Errorf("oracle pnpm hash not found (build err: %v)", buildErr)
}
if err := internal.ReplaceOnceFunc(oracleFile, pnpmRe, func(s string) string {
return regexp.MustCompile(`hash = "sha256-[^"]+";`).ReplaceAllString(s, fmt.Sprintf(`hash = "%s";`, pnpmHash))
}); err != nil {
return err
}
return nil
}
func main() {
repoRoot, err := os.Getwd()
if err != nil {
@ -226,6 +147,26 @@ func main() {
}
tools := []Tool{
{
Name: "discrawl",
Repo: "steipete/discrawl",
Assets: []AssetSpec{
{System: "aarch64-darwin", Regex: regexp.MustCompile(`discrawl_[0-9.]+_darwin_arm64\.tar\.gz`)},
{System: "x86_64-linux", Regex: regexp.MustCompile(`discrawl_[0-9.]+_linux_amd64\.tar\.gz`)},
{System: "aarch64-linux", Regex: regexp.MustCompile(`discrawl_[0-9.]+_linux_arm64\.tar\.gz`)},
},
NixFile: filepath.Join(repoRoot, "nix", "pkgs", "discrawl.nix"),
},
{
Name: "wacrawl",
Repo: "steipete/wacrawl",
Assets: []AssetSpec{
{System: "aarch64-darwin", Regex: regexp.MustCompile(`wacrawl_[0-9.]+_darwin_arm64\.tar\.gz`)},
{System: "x86_64-linux", Regex: regexp.MustCompile(`wacrawl_[0-9.]+_linux_amd64\.tar\.gz`)},
{System: "aarch64-linux", Regex: regexp.MustCompile(`wacrawl_[0-9.]+_linux_arm64\.tar\.gz`)},
},
NixFile: filepath.Join(repoRoot, "nix", "pkgs", "wacrawl.nix"),
},
{
Name: "gogcli",
Repo: "steipete/gogcli",
@ -236,6 +177,17 @@ func main() {
},
NixFile: filepath.Join(repoRoot, "nix", "pkgs", "gogcli.nix"),
},
{
Name: "goplaces",
Repo: "steipete/goplaces",
Assets: []AssetSpec{
{System: "aarch64-darwin", Regex: regexp.MustCompile(`goplaces_[0-9.]+_darwin_arm64\.tar\.gz`)},
{System: "x86_64-darwin", Regex: regexp.MustCompile(`goplaces_[0-9.]+_darwin_amd64\.tar\.gz`)},
{System: "x86_64-linux", Regex: regexp.MustCompile(`goplaces_[0-9.]+_linux_amd64\.tar\.gz`)},
{System: "aarch64-linux", Regex: regexp.MustCompile(`goplaces_[0-9.]+_linux_arm64\.tar\.gz`)},
},
NixFile: filepath.Join(repoRoot, "nix", "pkgs", "goplaces.nix"),
},
{
Name: "camsnap",
Repo: "steipete/camsnap",
@ -256,20 +208,11 @@ func main() {
},
NixFile: filepath.Join(repoRoot, "nix", "pkgs", "sonoscli.nix"),
},
{
Name: "bird",
Repo: "steipete/bird",
Optional: true, // repo got nuked; keep packaging pinned, but don't fail the updater
Assets: []AssetSpec{
{System: "aarch64-darwin", Regex: regexp.MustCompile(`bird-macos-universal-v[0-9.]+\.tar\.gz`)},
},
NixFile: filepath.Join(repoRoot, "nix", "pkgs", "bird.nix"),
},
{
Name: "peekaboo",
Repo: "steipete/peekaboo",
Assets: []AssetSpec{
{System: "aarch64-darwin", Regex: regexp.MustCompile(`peekaboo-macos-universal\.tar\.gz`)},
{System: "aarch64-darwin", Regex: regexp.MustCompile(`peekaboo-macos-(?:arm64|universal)\.tar\.gz`)},
},
NixFile: filepath.Join(repoRoot, "nix", "pkgs", "peekaboo.nix"),
},
@ -313,9 +256,4 @@ func main() {
}
}
if err := updateOracle(repoRoot); err != nil {
// Oracle releases occasionally ship with an out-of-date pnpm-lock.yaml.
// In that case, we keep the previously pinned version and still update other tools.
log.Printf("[update-tools] skipping oracle update: %v", err)
}
}

View File

@ -12,16 +12,17 @@
forAllSystems = f: lib.genAttrs systems (system: f system);
packageSystems = {
summarize = [ "aarch64-darwin" "x86_64-linux" "aarch64-linux" ];
discrawl = [ "aarch64-darwin" "x86_64-linux" "aarch64-linux" ];
wacrawl = [ "aarch64-darwin" "x86_64-linux" "aarch64-linux" ];
gogcli = [ "aarch64-darwin" "x86_64-linux" "aarch64-linux" ];
goplaces = [ "aarch64-darwin" "x86_64-linux" "aarch64-linux" ];
camsnap = [ "aarch64-darwin" "x86_64-linux" "aarch64-linux" ];
sonoscli = [ "aarch64-darwin" "x86_64-linux" "aarch64-linux" ];
bird = [ "aarch64-darwin" ];
peekaboo = [ "aarch64-darwin" ];
poltergeist = [ "aarch64-darwin" ];
sag = [ "aarch64-darwin" "x86_64-linux" ];
imsg = [ "aarch64-darwin" ];
oracle = [ "aarch64-darwin" "x86_64-linux" "aarch64-linux" ];
codexbar-app = [ "aarch64-darwin" ];
};
in {
packages = forAllSystems (system:
@ -36,6 +37,12 @@
nodejs = if pkgs ? nodejs_22 then pkgs.nodejs_22 else pkgs.nodejs;
};
})
// (lib.optionalAttrs (supports "discrawl") {
discrawl = pkgs.callPackage ./nix/pkgs/discrawl.nix {};
})
// (lib.optionalAttrs (supports "wacrawl") {
wacrawl = pkgs.callPackage ./nix/pkgs/wacrawl.nix {};
})
// (lib.optionalAttrs (supports "gogcli") {
gogcli = pkgs.callPackage ./nix/pkgs/gogcli.nix {};
})
@ -48,9 +55,6 @@
// (lib.optionalAttrs (supports "sonoscli") {
sonoscli = pkgs.callPackage ./nix/pkgs/sonoscli.nix {};
})
// (lib.optionalAttrs (supports "bird") {
bird = pkgs.callPackage ./nix/pkgs/bird.nix {};
})
// (lib.optionalAttrs (supports "peekaboo") {
peekaboo = pkgs.callPackage ./nix/pkgs/peekaboo.nix {};
})
@ -63,14 +67,16 @@
// (lib.optionalAttrs (supports "imsg") {
imsg = pkgs.callPackage ./nix/pkgs/imsg.nix {};
})
// (lib.optionalAttrs (supports "oracle") {
oracle = pkgs.callPackage ./nix/pkgs/oracle.nix {
pkgs = pkgs;
pnpm = if pkgs ? pnpm_10 then pkgs.pnpm_10 else pkgs.pnpm;
};
// (lib.optionalAttrs (supports "codexbar-app") {
codexbar-app = pkgs.callPackage ./nix/pkgs/codexbar-app.nix {};
})
);
homeManagerModules = {
codexbar = import ./nix/modules/home-manager/codexbar.nix { inherit self; };
default = self.homeManagerModules.codexbar;
};
checks = forAllSystems (system: self.packages.${system});
};
}

View File

@ -61,15 +61,6 @@ func PrefetchGitHub(owner, repo, rev string) (string, error) {
return res.Hash, nil
}
func NixBuildOracle() (string, error) {
cmd := exec.Command("nix", "build", ".#oracle")
var out bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &out
err := cmd.Run()
return out.String(), err
}
func NixBuildSummarize() (string, error) {
return NixBuildSummarizeSystem("")
}

View File

@ -0,0 +1,88 @@
{ self }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) mkEnableOption mkIf mkOption types;
cfg = config.programs.codexbar;
system = pkgs.stdenv.hostPlatform.system;
defaultPackage =
if self.packages ? ${system} && self.packages.${system} ? codexbar-app then
self.packages.${system}.codexbar-app
else
null;
in
{
options.programs.codexbar = {
enable = mkEnableOption "CodexBar macOS menu bar app";
package = mkOption {
type = types.nullOr types.package;
default = defaultPackage;
defaultText = lib.literalExpression "inputs.nix-steipete-tools.packages.\${pkgs.system}.codexbar-app";
description = "CodexBar app package.";
};
launchd = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
Configure a launchd agent to manage the CodexBar process.
CodexBar can also manage launch-at-login from inside the app. Enable this
option when you want Home Manager and launchd to own startup instead.
'';
};
keepAlive = mkOption {
type = types.bool;
default = false;
description = "Whether launchd should restart CodexBar after it exits.";
};
environmentVariables = mkOption {
type = types.attrsOf types.str;
default = {
PATH = "${config.home.profileDirectory}/bin:/usr/bin:/bin:/usr/sbin:/sbin";
};
defaultText = lib.literalExpression ''
{
PATH = "\${config.home.profileDirectory}/bin:/usr/bin:/bin:/usr/sbin:/sbin";
}
'';
description = "Environment variables passed to the CodexBar launchd agent.";
};
};
};
config = mkIf cfg.enable {
assertions = [
(lib.hm.assertions.assertPlatform "programs.codexbar" pkgs lib.platforms.darwin)
{
assertion = cfg.package != null;
message = "programs.codexbar.package must be set on this platform.";
}
{
assertion = !cfg.launchd.enable || config.launchd.enable;
message = "programs.codexbar.launchd.enable requires launchd.enable.";
}
];
home.packages = lib.mkIf (cfg.package != null) [ cfg.package ];
launchd.agents.codexbar = mkIf cfg.launchd.enable {
enable = true;
config = {
ProgramArguments = [ "${cfg.package}/Applications/CodexBar.app/Contents/MacOS/CodexBar" ];
ProcessType = "Interactive";
RunAtLoad = true;
KeepAlive = cfg.launchd.keepAlive;
EnvironmentVariables = cfg.launchd.environmentVariables;
};
};
};
}

37
nix/pkgs/codexbar-app.nix Normal file
View File

@ -0,0 +1,37 @@
{
lib,
stdenvNoCC,
fetchzip,
}:
stdenvNoCC.mkDerivation {
pname = "codexbar-app";
version = "0.23";
src = fetchzip {
url = "https://github.com/steipete/CodexBar/releases/download/v0.23/CodexBar-0.23.zip";
hash = "sha256-4LtZZzoP9tofNhy8jVkaopE+aivn0PRBEeJBGpDnbE8=";
stripRoot = false;
};
dontUnpack = true;
installPhase = ''
runHook preInstall
mkdir -p "$out/Applications"
app_path="$(find "$src" -maxdepth 2 -name 'CodexBar.app' -print -quit)"
if [ -z "$app_path" ]; then
echo "CodexBar.app not found in $src" >&2
exit 1
fi
cp -R "$app_path" "$out/Applications/CodexBar.app"
runHook postInstall
'';
meta = with lib; {
description = "CodexBar macOS menu bar app bundle";
homepage = "https://github.com/steipete/CodexBar";
license = licenses.mit;
platforms = [ "aarch64-darwin" ];
};
}

53
nix/pkgs/discrawl.nix Normal file
View File

@ -0,0 +1,53 @@
{ lib, stdenv, fetchurl }:
let
sources = {
"aarch64-darwin" = {
url = "https://github.com/steipete/discrawl/releases/download/v0.6.5/discrawl_0.6.5_darwin_arm64.tar.gz";
hash = "sha256-nxt8Pq0ldbf4QefkBYDMIEzp9dRZUBuK7GBygI+4HDc=";
};
"x86_64-linux" = {
url = "https://github.com/steipete/discrawl/releases/download/v0.6.5/discrawl_0.6.5_linux_amd64.tar.gz";
hash = "sha256-0lLlZYWZgvebBNWfZjicdB+o9tJ43N4n3CxAQ9mLfHM=";
};
"aarch64-linux" = {
url = "https://github.com/steipete/discrawl/releases/download/v0.6.5/discrawl_0.6.5_linux_arm64.tar.gz";
hash = "sha256-k+ls+9dKvBLaMbThCoNgn6vQxGhviDeXaH6JmbeBrLM=";
};
};
in
stdenv.mkDerivation {
pname = "discrawl";
version = "0.6.5";
src = fetchurl sources.${stdenv.hostPlatform.system};
dontConfigure = true;
dontBuild = true;
unpackPhase = ''
tar -xzf "$src"
'';
installPhase = ''
runHook preInstall
mkdir -p "$out/bin" "$out/share/doc/discrawl"
cp $(find . -type f -name discrawl | head -1) "$out/bin/discrawl"
chmod 0755 "$out/bin/discrawl"
if [ -f LICENSE ]; then
cp LICENSE "$out/share/doc/discrawl/"
fi
if [ -f README.md ]; then
cp README.md "$out/share/doc/discrawl/"
fi
runHook postInstall
'';
meta = with lib; {
description = "Mirror Discord into SQLite and search server history locally";
homepage = "https://github.com/steipete/discrawl";
license = licenses.mit;
platforms = builtins.attrNames sources;
mainProgram = "discrawl";
};
}

View File

@ -3,22 +3,22 @@
let
sources = {
"aarch64-darwin" = {
url = "https://github.com/steipete/gogcli/releases/download/v0.10.0/gogcli_0.10.0_darwin_arm64.tar.gz";
hash = "sha256-sNPDBSmr40X1Epzm7ZRzJfcDHU7p70kLHl12/5c4/J8=";
url = "https://github.com/steipete/gogcli/releases/download/v0.14.0/gogcli_0.14.0_darwin_arm64.tar.gz";
hash = "sha256-qJ+H7dc+oPn7E57kvEIwlAc990nkpiGSqlFCDy9kZjo=";
};
"x86_64-linux" = {
url = "https://github.com/steipete/gogcli/releases/download/v0.10.0/gogcli_0.10.0_linux_amd64.tar.gz";
hash = "sha256-f8DVB7LQprzprE6IyGu3jZo2ckRXEu9FiomMM7KqIV0=";
url = "https://github.com/steipete/gogcli/releases/download/v0.14.0/gogcli_0.14.0_linux_amd64.tar.gz";
hash = "sha256-sq2qUDYnqlbZGGzxBHp5CqFfjdGFIkgN1P8UBgyd0hs=";
};
"aarch64-linux" = {
url = "https://github.com/steipete/gogcli/releases/download/v0.10.0/gogcli_0.10.0_linux_arm64.tar.gz";
hash = "sha256-Q48xgr47bF3Om3kyKm6tnH9nOW9g0Em7i0o5AEhPZRU=";
url = "https://github.com/steipete/gogcli/releases/download/v0.14.0/gogcli_0.14.0_linux_arm64.tar.gz";
hash = "sha256-KOq4AyYyjUvL6tMq4WtOZu2WYTdtJR1g44uFmJt8oHs=";
};
};
in
stdenv.mkDerivation {
pname = "gogcli";
version = "0.10.0";
version = "0.14.0";
src = fetchurl sources.${stdenv.hostPlatform.system};

View File

@ -1,31 +1,60 @@
{ lib, buildGoModule, fetchFromGitHub }:
{ lib, stdenv, fetchurl }:
buildGoModule rec {
pname = "goplaces";
version = "0.2.2-dev";
src = fetchFromGitHub {
owner = "joshp123";
repo = "goplaces";
rev = "a71fe3de986a78607d923f397113d7eb1babc111";
hash = "sha256-Lufp9+fwcoluNZR9iDYoIJ1yu3sM/8Q/EOkAlq5VLdk=";
let
sources = {
"aarch64-darwin" = {
url = "https://github.com/steipete/goplaces/releases/download/v0.4.0/goplaces_0.4.0_darwin_arm64.tar.gz";
hash = "sha256-FJ8hVqQhp/Ppk17CrDJk4dmu2pvS2xBX5cAzafpaM8g=";
};
"x86_64-darwin" = {
url = "https://github.com/steipete/goplaces/releases/download/v0.4.0/goplaces_0.4.0_darwin_amd64.tar.gz";
hash = "sha256-/vVK0hqh1q3tliSy4mRT9X3jRayYhWAfZLS+VsFd+NU=";
};
"x86_64-linux" = {
url = "https://github.com/steipete/goplaces/releases/download/v0.4.0/goplaces_0.4.0_linux_amd64.tar.gz";
hash = "sha256-cUHDAZ4Ep50K2ljLn8LyJeWPJlcr/sytP95UsNmSv3w=";
};
"aarch64-linux" = {
url = "https://github.com/steipete/goplaces/releases/download/v0.4.0/goplaces_0.4.0_linux_arm64.tar.gz";
hash = "sha256-YjeLRDLwuQbUOYZYnpNkuYDvsm54PB1jmqTHas2T5VY=";
};
};
subPackages = [ "cmd/goplaces" ];
ldflags = [
"-s"
"-w"
"-X github.com/steipete/goplaces/internal/cli.Version=${version}"
];
vendorHash = "sha256-OFTjLtKwYSy4tM+D12mqI28M73YJdG4DyqPkXS7ZKUg=";
meta = with lib; {
description = "Modern Go client + CLI for the Google Places API";
homepage = "https://github.com/joshp123/goplaces";
description = "Modern Go client + CLI for the Google Places API (New)";
homepage = "https://github.com/steipete/goplaces";
license = licenses.mit;
platforms = platforms.darwin ++ platforms.linux;
platforms = builtins.attrNames sources;
mainProgram = "goplaces";
};
in
stdenv.mkDerivation {
pname = "goplaces";
version = "0.4.0";
src = fetchurl sources.${stdenv.hostPlatform.system};
dontConfigure = true;
dontBuild = true;
unpackPhase = ''
tar -xzf "$src"
'';
installPhase = ''
runHook preInstall
mkdir -p "$out/bin" "$out/share/doc/goplaces"
cp $(find . -type f -name goplaces | head -1) "$out/bin/goplaces"
chmod 0755 "$out/bin/goplaces"
if [ -f LICENSE ]; then
cp LICENSE "$out/share/doc/goplaces/"
fi
if [ -f README.md ]; then
cp README.md "$out/share/doc/goplaces/"
fi
runHook postInstall
'';
inherit meta;
}

View File

@ -3,14 +3,14 @@
let
sources = {
"aarch64-darwin" = {
url = "https://github.com/steipete/imsg/releases/download/v0.4.0/imsg-macos.zip";
hash = "sha256-0OXjM+6IGS1ZW/7Z7s5g417K0DABRZZtWtJ0WMM+QHs=";
url = "https://github.com/steipete/imsg/releases/download/v0.5.0/imsg-macos.zip";
hash = "sha256-rirJZ+8eQf0Ea3RXD4bHrWljSpGEb+wtbMdrsEtHPAo=";
};
};
in
stdenv.mkDerivation {
pname = "imsg";
version = "0.4.0";
version = "0.5.0";
src = fetchurl sources.${stdenv.hostPlatform.system};

View File

@ -1,128 +0,0 @@
{ lib
, stdenv
, fetchurl
, fetchFromGitHub
, nodejs
, pnpm
, python3
, python3Packages
, pkg-config
, makeWrapper
, pkgs
, zstd
}:
let
pnpmFetchDepsPkg = pkgs.callPackage "${pkgs.path}/pkgs/build-support/node/fetch-pnpm-deps" {
inherit pnpm;
};
in
stdenv.mkDerivation (finalAttrs: {
pname = "oracle";
version = "0.8.5";
srcTarball = fetchurl {
url = "https://github.com/steipete/oracle/releases/download/v0.8.5/oracle-0.8.5.tgz";
hash = "sha256-MSb1+5wEHK38iq+yob7Tz7xov0Wh9zHcmXGs/l2KdMA=";
};
lockSrc = fetchFromGitHub {
owner = "steipete";
repo = "oracle";
rev = "v0.8.5";
hash = "sha256-q1l3IcVAj7Gb8Lp0JzQakLRg2AlVrJniMBHRwxKQVbM=";
};
srcPatched = stdenv.mkDerivation {
name = "oracle-src-patched";
src = finalAttrs.srcTarball;
nativeBuildInputs = [ python3 ];
dontConfigure = true;
dontBuild = true;
unpackPhase = ''
tar -xzf "$src"
'';
installPhase = ''
mkdir -p "$out"
if [ -d package ]; then
shopt -s dotglob
mv package/* "$out"/
else
cp -R . "$out"/
fi
cp -f "${finalAttrs.lockSrc}/pnpm-lock.yaml" "$out/pnpm-lock.yaml"
export OUT_DIR="$out"
python3 - <<'PY'
import json
import os
from pathlib import Path
path = Path(os.environ["OUT_DIR"]) / "package.json"
data = json.loads(path.read_text())
data.pop("packageManager", None)
path.write_text(json.dumps(data, indent=2) + "\n")
PY
'';
};
src = finalAttrs.srcPatched;
pnpmDeps = (pnpmFetchDepsPkg.fetchPnpmDeps {
pname = finalAttrs.pname;
version = finalAttrs.version;
src = finalAttrs.srcPatched;
hash = "sha256-Tmwe55l4QMzXsO7F1kUSnGuZjkuMtDORNtqKnWZ/HrA=";
fetcherVersion = 3;
});
nativeBuildInputs = [
nodejs
pnpm
python3
python3Packages.setuptools
pkg-config
makeWrapper
zstd
];
env = {
PNPM_IGNORE_PACKAGE_MANAGER_CHECK = "1";
CI = "1";
HOME = "/tmp";
PNPM_HOME = "/tmp/pnpm-home";
PNPM_CONFIG_HOME = "/tmp/pnpm-config";
XDG_CACHE_HOME = "/tmp/pnpm-cache";
NPM_CONFIG_USERCONFIG = "/tmp/pnpm-config/.npmrc";
npm_config_nodedir = "${nodejs.dev}";
npm_config_build_from_source = "1";
PNPM_CONFIG_IGNORE_SCRIPTS = "1";
};
buildPhase = ''
runHook preBuild
mkdir -p "$HOME" "$PNPM_HOME" "$PNPM_CONFIG_HOME" "$XDG_CACHE_HOME"
export PNPM_STORE_PATH="$TMPDIR/pnpm-store"
mkdir -p "$PNPM_STORE_PATH"
tar --zstd -xf ${finalAttrs.pnpmDeps}/pnpm-store.tar.zst -C "$PNPM_STORE_PATH"
pnpm install --offline --no-frozen-lockfile --store-dir "$PNPM_STORE_PATH" --ignore-scripts
runHook postBuild
'';
installPhase = ''
runHook preInstall
mkdir -p "$out/libexec" "$out/bin"
cp -r dist package.json vendor assets-oracle-icon.png node_modules "$out/libexec/"
chmod 0755 "$out/libexec/dist/bin/oracle-cli.js" "$out/libexec/dist/bin/oracle-mcp.js"
ln -s "$out/libexec/dist/bin/oracle-cli.js" "$out/bin/oracle"
ln -s "$out/libexec/dist/bin/oracle-mcp.js" "$out/bin/oracle-mcp"
runHook postInstall
'';
meta = with lib; {
description = "Bundle prompts + files for second-model review";
homepage = "https://github.com/steipete/oracle";
license = licenses.mit;
platforms = [ "aarch64-darwin" "x86_64-linux" "aarch64-linux" ];
mainProgram = "oracle";
};
})

View File

@ -3,14 +3,14 @@
let
sources = {
"aarch64-darwin" = {
url = "https://github.com/steipete/Peekaboo/releases/download/v3.0.0-beta3/peekaboo-macos-universal.tar.gz";
hash = "sha256-d+rfb9XFTqxktIRNXMiHiQttb0XUmvYbBcbinqLL0kU=";
url = "https://github.com/steipete/Peekaboo/releases/download/v3.0.0-beta4/peekaboo-macos-arm64.tar.gz";
hash = "sha256-74eXVHpRAmcs0mzK3GLh/3So78AEMZzXBvx1Zg7uOkc=";
};
};
in
stdenv.mkDerivation {
pname = "peekaboo";
version = "3.0.0-beta3";
version = "3.0.0-beta4";
src = fetchurl sources.${stdenv.hostPlatform.system};

View File

@ -1,4 +1,4 @@
{ lib, stdenv, fetchurl, watchman }:
{ lib, stdenv, fetchurl }:
let
sources = {
@ -30,8 +30,6 @@ stdenv.mkDerivation {
runHook postInstall
'';
propagatedBuildInputs = [ watchman ];
meta = with lib; {
description = "Universal file watcher with auto-rebuild for any language or build system";
homepage = "https://github.com/steipete/poltergeist";

View File

@ -15,17 +15,17 @@
let
pname = "summarize";
version = "0.11.1";
version = "0.14.1";
binSources = {
"aarch64-darwin" = {
url = "https://github.com/steipete/summarize/releases/download/v0.11.1/summarize-macos-arm64-v0.11.1.tar.gz";
hash = "sha256-RJNeCxWfbMCOrD6ABR4wSALdtl1sImS4Wkjz1TdPmTE=";
url = "https://github.com/steipete/summarize/releases/download/v0.14.1/summarize-macos-arm64-v0.14.1.tar.gz";
hash = "sha256-X3aBGiAjoOSIINxHyGOzg/b88fpCZQcv0cM2L0KqS8c=";
};
};
src = fetchurl {
url = "https://github.com/steipete/summarize/archive/refs/tags/v${version}.tar.gz";
hash = "sha256-2s3XAy9evHRZQthTWRhIjkv7Z40K5PiUFHEIGo6XxBs=";
hash = "sha256-UAf7IXerk5VM3DyLyMD259Dh55ZjcZPK/3T2kPj9RO4=";
};
pnpmFetchDepsPkg = pkgs.callPackage "${pkgs.path}/pkgs/build-support/node/fetch-pnpm-deps" {
@ -36,7 +36,7 @@ let
pname = pname;
version = version;
src = src;
hash = "sha256-vqTXJ64DpanUwdUlSS3Fa+zy70qIGgsNtKzYF82J1RU=";
hash = "sha256-s1I3TTX3uw/iJpbWvoNE5kg9XQVjw0mnA5pRTVs7ypY=";
fetcherVersion = 3;
});
@ -72,7 +72,7 @@ if stdenv.isLinux then
PNPM_CONFIG_HOME = "/tmp/pnpm-config";
XDG_CACHE_HOME = "/tmp/pnpm-cache";
NPM_CONFIG_USERCONFIG = "/tmp/pnpm-config/.npmrc";
npm_config_nodedir = "${nodejs.dev}";
npm_config_nodedir = "${lib.getDev nodejs}";
npm_config_build_from_source = "1";
PNPM_CONFIG_IGNORE_SCRIPTS = "1";
PNPM_CONFIG_MANAGE_PACKAGE_MANAGER_VERSIONS = "false";
@ -120,6 +120,7 @@ if stdenv.isLinux then
runHook preInstall
mkdir -p "$out/libexec" "$out/libexec/packages" "$out/libexec/apps" "$out/bin"
cp -r dist node_modules "$out/libexec/"
find "$out/libexec/node_modules" -name ".pnpm-workspace-state-v1.json" -delete
cp -r packages/core "$out/libexec/packages/"
cp -r apps/chrome-extension "$out/libexec/apps/"
chmod 0755 "$out/libexec/dist/cli.js"

53
nix/pkgs/wacrawl.nix Normal file
View File

@ -0,0 +1,53 @@
{ lib, stdenv, fetchurl }:
let
sources = {
"aarch64-darwin" = {
url = "https://github.com/steipete/wacrawl/releases/download/v0.2.0/wacrawl_0.2.0_darwin_arm64.tar.gz";
hash = "sha256-Pk3G8ZBsZOSU9PNgtuJPzQC2XDe71yUSkPHXxqGL+vQ=";
};
"x86_64-linux" = {
url = "https://github.com/steipete/wacrawl/releases/download/v0.2.0/wacrawl_0.2.0_linux_amd64.tar.gz";
hash = "sha256-xZBLpgzLG+3NClMwanZ2V79Qqw36acpT2MI5z3gfHoo=";
};
"aarch64-linux" = {
url = "https://github.com/steipete/wacrawl/releases/download/v0.2.0/wacrawl_0.2.0_linux_arm64.tar.gz";
hash = "sha256-oiPgR7yWd5aYTYqlIZST89ELSdetW6nvTaE8rl78ZTc=";
};
};
in
stdenv.mkDerivation {
pname = "wacrawl";
version = "0.2.0";
src = fetchurl sources.${stdenv.hostPlatform.system};
dontConfigure = true;
dontBuild = true;
unpackPhase = ''
tar -xzf "$src"
'';
installPhase = ''
runHook preInstall
mkdir -p "$out/bin" "$out/share/doc/wacrawl"
cp $(find . -type f -name wacrawl | head -1) "$out/bin/wacrawl"
chmod 0755 "$out/bin/wacrawl"
if [ -f LICENSE ]; then
cp LICENSE "$out/share/doc/wacrawl/"
fi
if [ -f README.md ]; then
cp README.md "$out/share/doc/wacrawl/"
fi
runHook postInstall
'';
meta = with lib; {
description = "Read-only local archive and search for WhatsApp Desktop data";
homepage = "https://github.com/steipete/wacrawl";
license = licenses.mit;
platforms = builtins.attrNames sources;
mainProgram = "wacrawl";
};
}

38
tools/discrawl/flake.nix Normal file
View File

@ -0,0 +1,38 @@
{
description = "openclaw plugin: discrawl";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs?rev=16c7794d0a28b5a37904d55bcca36003b9109aaa&narHash=sha256-fFUnEYMla8b7UKjijLnMe%2BoVFOz6HjijGGNS1l7dYaQ%3D";
root.url = "path:../..";
};
outputs = { self, nixpkgs, root }:
let
lib = nixpkgs.lib;
systems = builtins.attrNames root.packages;
pluginFor = system:
let
packagesForSystem = root.packages.${system} or {};
discrawl = packagesForSystem.discrawl or null;
in
if discrawl == null then null else {
name = "discrawl";
skills = [ ./skills/discrawl ];
packages = [ discrawl ];
needs = {
stateDirs = [ ".discrawl" ];
requiredEnv = [ ];
};
};
in {
packages = lib.genAttrs systems (system:
let
discrawl = (root.packages.${system} or {}).discrawl or null;
in
if discrawl == null then {}
else { discrawl = discrawl; }
);
openclawPlugin = pluginFor;
};
}

View File

@ -0,0 +1,74 @@
---
name: discrawl
description: Mirror Discord guild history into local SQLite and query it offline with search, messages, mentions, reports, and DM wiretap import.
homepage: https://github.com/steipete/discrawl
metadata:
{
"openclaw":
{
"emoji": "🛰️",
"requires": { "bins": ["discrawl"] },
"install":
[
{
"id": "brew",
"kind": "brew",
"formula": "steipete/tap/discrawl",
"bins": ["discrawl"],
"label": "Install discrawl (brew)",
},
],
},
}
---
# discrawl
Use `discrawl` to mirror Discord guild data into local SQLite, then query it offline.
## When to Use
Use this skill when the user wants to:
- search Discord history locally without relying on Discord search
- archive a guild into SQLite for later queries
- inspect recent messages, mentions, channels, or members from a local archive
- import local Discord Desktop cache data for DM recovery/search
- publish or subscribe to a Git-backed Discord archive snapshot
## Requirements
- Discord bot token for guild sync, or an existing OpenClaw Discord config
- local Discord Desktop cache files only if using `wiretap`
- enough local disk for SQLite archive growth
## Setup
- Default config: `~/.discrawl/config.toml`
- Default database: `~/.discrawl/discrawl.db`
- Fastest setup when OpenClaw already has Discord configured:
- `discrawl init --from-openclaw ~/.openclaw/openclaw.json`
- Env-only setup:
- `export DISCORD_BOT_TOKEN="..."`
- `discrawl init`
## Common Commands
- Doctor: `discrawl doctor`
- Initial history: `discrawl sync --full`
- Incremental refresh: `discrawl sync`
- Live tail: `discrawl tail`
- Search: `discrawl search "panic nil pointer"`
- Recent channel messages: `discrawl messages --channel general --hours 24`
- Mentions: `discrawl mentions --user <user-id>`
- DM cache import: `discrawl wiretap`
- Local DM search: `discrawl dms --search "launch checklist"`
- Read-only SQL: `discrawl sql "select count(*) from messages"`
- Git-backed reader mode: `discrawl subscribe <private-repo-url>`
## Notes
- Bot-token sync reads only guilds/channels the bot can access.
- `wiretap` uses local Discord Desktop cache files only; it does not use a user token.
- Prefer `discrawl doctor` before a first sync.
- Use `sync --full` once for backfill, then plain `sync` for routine refreshes.

65
tools/gogcli/flake.lock generated Normal file
View File

@ -0,0 +1,65 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1767364772,
"narHash": "sha256-fFUnEYMla8b7UKjijLnMe+oVFOz6HjijGGNS1l7dYaQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "16c7794d0a28b5a37904d55bcca36003b9109aaa",
"type": "github"
},
"original": {
"narHash": "sha256-fFUnEYMla8b7UKjijLnMe+oVFOz6HjijGGNS1l7dYaQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "16c7794d0a28b5a37904d55bcca36003b9109aaa",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1767364772,
"narHash": "sha256-fFUnEYMla8b7UKjijLnMe+oVFOz6HjijGGNS1l7dYaQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "16c7794d0a28b5a37904d55bcca36003b9109aaa",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"root": "root_2"
}
},
"root_2": {
"inputs": {
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1772109602,
"narHash": "sha256-Lzs6tzCkOOtDaZIRSGvS6ykJOtu0hZrdI95kxhkSIuw=",
"owner": "openclaw",
"repo": "nix-steipete-tools",
"rev": "eddb00d4c1037a983913fccf32880aece050cd32",
"type": "github"
},
"original": {
"narHash": "sha256-Lzs6tzCkOOtDaZIRSGvS6ykJOtu0hZrdI95kxhkSIuw=",
"owner": "openclaw",
"repo": "nix-steipete-tools",
"rev": "eddb00d4c1037a983913fccf32880aece050cd32",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View File

@ -3,7 +3,7 @@
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs?rev=16c7794d0a28b5a37904d55bcca36003b9109aaa&narHash=sha256-fFUnEYMla8b7UKjijLnMe%2BoVFOz6HjijGGNS1l7dYaQ%3D";
root.url = "github:openclaw/nix-steipete-tools?rev=dbf0a31a57407d9140e32357ea8d0215bd9feed9&narHash=sha256-QkPl/Rgk9DXgaVNhjvHHHjy5e81j+MzcVOouZRdUTLA=";
root.url = "github:openclaw/nix-steipete-tools?rev=eddb00d4c1037a983913fccf32880aece050cd32&narHash=sha256-Lzs6tzCkOOtDaZIRSGvS6ykJOtu0hZrdI95kxhkSIuw=";
};
outputs = { self, nixpkgs, root }:

65
tools/goplaces/flake.lock generated Normal file
View File

@ -0,0 +1,65 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1767364772,
"narHash": "sha256-fFUnEYMla8b7UKjijLnMe+oVFOz6HjijGGNS1l7dYaQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "16c7794d0a28b5a37904d55bcca36003b9109aaa",
"type": "github"
},
"original": {
"narHash": "sha256-fFUnEYMla8b7UKjijLnMe+oVFOz6HjijGGNS1l7dYaQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "16c7794d0a28b5a37904d55bcca36003b9109aaa",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1767364772,
"narHash": "sha256-fFUnEYMla8b7UKjijLnMe+oVFOz6HjijGGNS1l7dYaQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "16c7794d0a28b5a37904d55bcca36003b9109aaa",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"root": "root_2"
}
},
"root_2": {
"inputs": {
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1770237944,
"narHash": "sha256-nfCSSyNU97XpKVPgo6mODBwrVeTOuMCl3i18QuGjpN0=",
"owner": "openclaw",
"repo": "nix-steipete-tools",
"rev": "6352c8247b3b889d7f17bce1f09d6c58fd34932c",
"type": "github"
},
"original": {
"narHash": "sha256-nfCSSyNU97XpKVPgo6mODBwrVeTOuMCl3i18QuGjpN0=",
"owner": "openclaw",
"repo": "nix-steipete-tools",
"rev": "6352c8247b3b889d7f17bce1f09d6c58fd34932c",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View File

@ -1,6 +1,6 @@
---
name: imsg
description: iMessage/SMS CLI for listing chats, history, watch, and sending.
description: iMessage/SMS CLI for listing chats, history, and sending messages via Messages.app.
homepage: https://imsg.to
metadata:
{
@ -23,52 +23,100 @@ metadata:
}
---
# imsg Actions
# imsg
## Overview
Use `imsg` to read and send iMessage/SMS via macOS Messages.app.
Use `imsg` to read and send Messages.app iMessage/SMS on macOS.
## When to Use
Requirements: Messages.app signed in, Full Disk Access for your terminal, and Automation permission to control Messages.app for sending.
✅ **USE this skill when:**
## Inputs to collect
- User explicitly asks to send iMessage or SMS
- Reading iMessage conversation history
- Checking recent Messages.app chats
- Sending to phone numbers or Apple IDs
- Recipient handle (phone/email) for `send`
- `chatId` for history/watch (from `imsg chats --limit 10 --json`)
- `text` and optional `file` path for sends
## When NOT to Use
## Actions
❌ **DON'T use this skill when:**
### List chats
- Telegram messages → use `message` tool with `channel:telegram`
- Signal messages → use Signal channel if configured
- WhatsApp messages → use WhatsApp channel if configured
- Discord messages → use `message` tool with `channel:discord`
- Slack messages → use `slack` skill
- Group chat management (adding/removing members) → not supported
- Bulk/mass messaging → always confirm with user first
- Replying in current conversation → just reply normally (OpenClaw routes automatically)
## Requirements
- macOS with Messages.app signed in
- Full Disk Access for terminal
- Automation permission for Messages.app (for sending)
## Common Commands
### List Chats
```bash
imsg chats --limit 10 --json
```
### Fetch chat history
### View History
```bash
# By chat ID
imsg history --chat-id 1 --limit 20 --json
# With attachments info
imsg history --chat-id 1 --limit 20 --attachments --json
```
### Watch a chat
### Watch for New Messages
```bash
imsg watch --chat-id 1 --attachments
```
### Send a message
### Send Messages
```bash
imsg send --to "+14155551212" --text "hi" --file /path/pic.jpg
# Text only
imsg send --to "+14155551212" --text "Hello!"
# With attachment
imsg send --to "+14155551212" --text "Check this out" --file /path/to/image.jpg
# Specify service
imsg send --to "+14155551212" --text "Hi" --service imessage
imsg send --to "+14155551212" --text "Hi" --service sms
```
## Notes
## Service Options
- `--service imessage|sms|auto` controls delivery.
- Confirm recipient + message before sending.
- `--service imessage` — Force iMessage (requires recipient has iMessage)
- `--service sms` — Force SMS (green bubble)
- `--service auto` — Let Messages.app decide (default)
## Ideas to try
## Safety Rules
- Use `imsg chats --limit 10 --json` to discover chat ids.
- Watch a high-signal chat to stream incoming messages.
1. **Always confirm recipient and message content** before sending
2. **Never send to unknown numbers** without explicit user approval
3. **Be careful with attachments** — confirm file path exists
4. **Rate limit yourself** — don't spam
## Example Workflow
User: "Text mom that I'll be late"
```bash
# 1. Find mom's chat
imsg chats --limit 20 --json | jq '.[] | select(.displayName | contains("Mom"))'
# 2. Confirm with user
# "Found Mom at +1555123456. Send 'I'll be late' via iMessage?"
# 3. Send after confirmation
imsg send --to "+1555123456" --text "I'll be late"
```

View File

@ -1,38 +0,0 @@
{
description = "openclaw plugin: oracle";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs?rev=16c7794d0a28b5a37904d55bcca36003b9109aaa&narHash=sha256-fFUnEYMla8b7UKjijLnMe%2BoVFOz6HjijGGNS1l7dYaQ%3D";
root.url = "github:openclaw/nix-steipete-tools?rev=dbf0a31a57407d9140e32357ea8d0215bd9feed9&narHash=sha256-QkPl/Rgk9DXgaVNhjvHHHjy5e81j+MzcVOouZRdUTLA=";
};
outputs = { self, nixpkgs, root }:
let
lib = nixpkgs.lib;
systems = builtins.attrNames root.packages;
pluginFor = system:
let
packagesForSystem = root.packages.${system} or {};
oracle = packagesForSystem.oracle or null;
in
if oracle == null then null else {
name = "oracle";
skills = [ ./skills/oracle ];
packages = [ oracle ];
needs = {
stateDirs = [];
requiredEnv = [];
};
};
in {
packages = lib.genAttrs systems (system:
let
oracle = (root.packages.${system} or {}).oracle or null;
in
if oracle == null then {}
else { oracle = oracle; }
);
openclawPlugin = pluginFor;
};
}

View File

@ -1,125 +0,0 @@
---
name: oracle
description: Best practices for using the oracle CLI (prompt + file bundling, engines, sessions, and file attachment patterns).
homepage: https://askoracle.dev
metadata:
{
"openclaw":
{
"emoji": "🧿",
"requires": { "bins": ["oracle"] },
"install":
[
{
"id": "node",
"kind": "node",
"package": "@steipete/oracle",
"bins": ["oracle"],
"label": "Install oracle (node)",
},
],
},
}
---
# oracle — best use
Oracle bundles your prompt + selected files into one “one-shot” request so another model can answer with real repo context (API or browser automation). Treat output as advisory: verify against code + tests.
## Main use case (browser, GPT5.2 Pro)
Default workflow here: `--engine browser` with GPT5.2 Pro in ChatGPT. This is the common “long think” path: ~10 minutes to ~1 hour is normal; expect a stored session you can reattach to.
Recommended defaults:
- Engine: browser (`--engine browser`)
- Model: GPT5.2 Pro (`--model gpt-5.2-pro` or `--model "5.2 Pro"`)
## Golden path
1. Pick a tight file set (fewest files that still contain the truth).
2. Preview payload + token spend (`--dry-run` + `--files-report`).
3. Use browser mode for the usual GPT5.2 Pro workflow; use API only when you explicitly want it.
4. If the run detaches/timeouts: reattach to the stored session (dont re-run).
## Commands (preferred)
- Help:
- `oracle --help`
- If the binary isnt installed: `npx -y @steipete/oracle --help` (avoid `pnpx` here; sqlite bindings).
- Preview (no tokens):
- `oracle --dry-run summary -p "<task>" --file "src/**" --file "!**/*.test.*"`
- `oracle --dry-run full -p "<task>" --file "src/**"`
- Token sanity:
- `oracle --dry-run summary --files-report -p "<task>" --file "src/**"`
- Browser run (main path; long-running is normal):
- `oracle --engine browser --model gpt-5.2-pro -p "<task>" --file "src/**"`
- Manual paste fallback:
- `oracle --render --copy -p "<task>" --file "src/**"`
- Note: `--copy` is a hidden alias for `--copy-markdown`.
## Attaching files (`--file`)
`--file` accepts files, directories, and globs. You can pass it multiple times; entries can be comma-separated.
- Include:
- `--file "src/**"`
- `--file src/index.ts`
- `--file docs --file README.md`
- Exclude:
- `--file "src/**" --file "!src/**/*.test.ts" --file "!**/*.snap"`
- Defaults (implementation behavior):
- Default-ignored dirs: `node_modules`, `dist`, `coverage`, `.git`, `.turbo`, `.next`, `build`, `tmp` (skipped unless explicitly passed as literal dirs/files).
- Honors `.gitignore` when expanding globs.
- Does not follow symlinks.
- Dotfiles filtered unless opted in via pattern (e.g. `--file ".github/**"`).
- Files > 1 MB rejected.
## Engines (API vs browser)
- Auto-pick: `api` when `OPENAI_API_KEY` is set; otherwise `browser`.
- Browser supports GPT + Gemini only; use `--engine api` for Claude/Grok/Codex or multi-model runs.
- Browser attachments:
- `--browser-attachments auto|never|always` (auto pastes inline up to ~60k chars then uploads).
- Remote browser host:
- Host: `oracle serve --host 0.0.0.0 --port 9473 --token <secret>`
- Client: `oracle --engine browser --remote-host <host:port> --remote-token <secret> -p "<task>" --file "src/**"`
## Sessions + slugs
- Stored under `~/.oracle/sessions` (override with `ORACLE_HOME_DIR`).
- Runs may detach or take a long time (browser + GPT5.2 Pro often does). If the CLI times out: dont re-run; reattach.
- List: `oracle status --hours 72`
- Attach: `oracle session <id> --render`
- Use `--slug "<3-5 words>"` to keep session IDs readable.
- Duplicate prompt guard exists; use `--force` only when you truly want a fresh run.
## Prompt template (high signal)
Oracle starts with **zero** project knowledge. Assume the model cannot infer your stack, build tooling, conventions, or “obvious” paths. Include:
- Project briefing (stack + build/test commands + platform constraints).
- “Where things live” (key directories, entrypoints, config files, boundaries).
- Exact question + what you tried + the error text (verbatim).
- Constraints (“dont change X”, “must keep public API”, etc).
- Desired output (“return patch plan + tests”, “give 3 options with tradeoffs”).
## Safety
- Dont attach secrets by default (`.env`, key files, auth tokens). Redact aggressively; share only whats required.
## “Exhaustive prompt” restoration pattern
For long investigations, write a standalone prompt + file set so you can rerun days later:
- 630 sentence project briefing + the goal.
- Repro steps + exact errors + what you tried.
- Attach all context files needed (entrypoints, configs, key modules, docs).
Oracle runs are one-shot; the model doesnt remember prior runs. “Restoring context” means re-running with the same prompt + `--file …` set (or reattaching a still-running stored session).

View File

@ -6,7 +6,7 @@ metadata:
{
"openclaw":
{
"emoji": "🗣️",
"emoji": "🔊",
"requires": { "bins": ["sag"], "env": ["ELEVENLABS_API_KEY"] },
"primaryEnv": "ELEVENLABS_API_KEY",
"install":
@ -68,7 +68,7 @@ Confirm voice + speaker before long output.
## Chat voice responses
When Peter asks for a "voice" reply (e.g., "crazy scientist voice", "explain in voice"), generate audio and send it:
When the user asks for a "voice" reply (e.g., "crazy scientist voice", "explain in voice"), generate audio and send it:
```bash
# Generate audio file

View File

@ -26,21 +26,40 @@ metadata:
Use `sonos` to control Sonos speakers on the local network.
Quick start
## Quick start
- `sonos discover`
- `sonos status --name "Kitchen"`
- `sonos play|pause|stop --name "Kitchen"`
- `sonos volume set 15 --name "Kitchen"`
Common tasks
## Common tasks
- Grouping: `sonos group status|join|unjoin|party|solo`
- Favorites: `sonos favorites list|open`
- Queue: `sonos queue list|play|clear`
- Spotify search (via SMAPI): `sonos smapi search --service "Spotify" --category tracks "query"`
Notes
## Notes
- If SSDP fails, specify `--ip <speaker-ip>`.
- Spotify Web API search is optional and requires `SPOTIFY_CLIENT_ID/SECRET`.
- If there is an error, check the troubleshooting section and offer advice if there is a decent match.
## Troubleshooting
### `sonos discover` - `no route to host`
- On error `Error: write udp4 0.0.0.0:64326->239.255.255.250:1900: sendto: no route to host (Command exited with code 1)`
- Note that the port number after `0.0.0.0:` is ephemeral and will change and the netmask may not match exactly either
- The `sendto: no route to host` should stay consistent
- Advise the user that in `direct` (no Docker sandbox) mode on Mac OS that Settings -> Privacy & Security -> Local Network will need to be enabled for the top-level host parent process of the Gateway
- `node` if running via `launchd`
- `Terminal` if running the gateway directly in the terminal
- `Visual Studio Code` if running via terminal in VS Code
- An alternative option is to use `sandbox` (docker container) with network access allowed for that sandbox
### `sonos discover` - `bind: operation not permitted`
- On error `Error: listen udp4 0.0.0.0:0: bind: operation not permitted`
- Advise the user that they may be running via a Codex or other sandbox that does not permit network access (this can be replicated by running `sonos discover` within a Codex CLI session with sandbox enabled and not approving the escalation request)

65
tools/summarize/flake.lock generated Normal file
View File

@ -0,0 +1,65 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1767364772,
"narHash": "sha256-fFUnEYMla8b7UKjijLnMe+oVFOz6HjijGGNS1l7dYaQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "16c7794d0a28b5a37904d55bcca36003b9109aaa",
"type": "github"
},
"original": {
"narHash": "sha256-fFUnEYMla8b7UKjijLnMe+oVFOz6HjijGGNS1l7dYaQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "16c7794d0a28b5a37904d55bcca36003b9109aaa",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1767364772,
"narHash": "sha256-fFUnEYMla8b7UKjijLnMe+oVFOz6HjijGGNS1l7dYaQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "16c7794d0a28b5a37904d55bcca36003b9109aaa",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"root": "root_2"
}
},
"root_2": {
"inputs": {
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1769797395,
"narHash": "sha256-QkPl/Rgk9DXgaVNhjvHHHjy5e81j+MzcVOouZRdUTLA=",
"owner": "openclaw",
"repo": "nix-steipete-tools",
"rev": "dbf0a31a57407d9140e32357ea8d0215bd9feed9",
"type": "github"
},
"original": {
"narHash": "sha256-QkPl/Rgk9DXgaVNhjvHHHjy5e81j+MzcVOouZRdUTLA=",
"owner": "openclaw",
"repo": "nix-steipete-tools",
"rev": "dbf0a31a57407d9140e32357ea8d0215bd9feed9",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View File

@ -1,6 +1,6 @@
---
name: summarize
description: Summarize or extract text/transcripts from URLs, podcasts, and local files (great fallback for “transcribe this YouTube/video”).
description: Summarize or transcribe URLs, YouTube/videos, podcasts, articles, transcripts, PDFs, and local files.
homepage: https://summarize.sh
metadata:
{

38
tools/wacrawl/flake.nix Normal file
View File

@ -0,0 +1,38 @@
{
description = "openclaw plugin: wacrawl";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs?rev=16c7794d0a28b5a37904d55bcca36003b9109aaa&narHash=sha256-fFUnEYMla8b7UKjijLnMe%2BoVFOz6HjijGGNS1l7dYaQ%3D";
root.url = "path:../..";
};
outputs = { self, nixpkgs, root }:
let
lib = nixpkgs.lib;
systems = builtins.attrNames root.packages;
pluginFor = system:
let
packagesForSystem = root.packages.${system} or {};
wacrawl = packagesForSystem.wacrawl or null;
in
if wacrawl == null then null else {
name = "wacrawl";
skills = [ ./skills/wacrawl ];
packages = [ wacrawl ];
needs = {
stateDirs = [ ".wacrawl" ];
requiredEnv = [ ];
};
};
in {
packages = lib.genAttrs systems (system:
let
wacrawl = (root.packages.${system} or {}).wacrawl or null;
in
if wacrawl == null then {}
else { wacrawl = wacrawl; }
);
openclawPlugin = pluginFor;
};
}

View File

@ -0,0 +1,69 @@
---
name: wacrawl
description: Read-only local archive and search for WhatsApp Desktop chats, messages, and media metadata.
homepage: https://github.com/steipete/wacrawl
metadata:
{
"openclaw":
{
"emoji": "💬",
"requires": { "bins": ["wacrawl"] },
"install":
[
{
"id": "brew",
"kind": "brew",
"formula": "steipete/tap/wacrawl",
"bins": ["wacrawl"],
"label": "Install wacrawl (brew)",
},
],
},
}
---
# wacrawl
Use `wacrawl` to snapshot local WhatsApp Desktop data into a separate SQLite archive and search it offline.
## When to Use
Use this skill when the user wants to:
- inspect local WhatsApp Desktop history without opening the app
- archive chats into a local SQLite database for repeat queries
- search WhatsApp messages locally with filters
- list chats, recent messages, or archive status from a read-only import
## Requirements
- local WhatsApp Desktop data on the same machine
- enough local disk for `~/.wacrawl/wacrawl.db`
- understand that this is read-only inspection, not message sending
## Setup
- Default source: `~/Library/Group Containers/group.net.whatsapp.WhatsApp.shared`
- Default archive DB: `~/.wacrawl/wacrawl.db`
- First sanity check:
- `wacrawl doctor`
- First import:
- `wacrawl import`
## Common Commands
- Doctor: `wacrawl doctor`
- Import fresh snapshot: `wacrawl import`
- Archive status: `wacrawl status`
- List chats: `wacrawl chats --limit 20`
- Recent messages: `wacrawl messages --limit 20`
- One chat: `wacrawl messages --chat 1234567890@s.whatsapp.net --limit 50`
- Search: `wacrawl search "release notes"`
- Filtered search: `wacrawl --json search "invoice" --from-them --after 2026-01-01`
## Notes
- `wacrawl` is read-only and does not send messages.
- It copies WhatsApp SQLite files into a temp snapshot before import.
- Use `--source` to override the WhatsApp Desktop container path.
- Use `--db` to archive somewhere other than `~/.wacrawl/wacrawl.db`.